scala – 运行时值的精简和存在类型
假设我想在一些字符串和整数标识符之间进行映射,我希望我的类型使得不可能得到运行时失败,因为有人试图查找超出范围的id.这是一个简单的API:
trait Vocab { def getId(value: String): Option[Int] def getValue(id: Int): Option[String] } 这是令人讨厌的,但如果用户通常会从getId获取其ID,因此知道它们是有效的.以下是这种意义上的改进: trait Vocab[Id] { def getId(value: String): Option[Id] def getValue(id: Id): String } 现在我们可以这样做: class TagId private(val value: Int) extends AnyVal object TagId { val tagCount: Int = 100 def fromInt(id: Int): Option[TagId] = if (id >= 0 && id < tagCount) Some(new TagId(id)) else None } 然后,我们的用户可以使用Vocab [TagId],而不必担心在典型情况下是否检查getValue查找失败,但是如果需要,它们仍然可以查找任意整数.然而,仍然很尴尬,因为我们必须为每种类型的事情写一个单独的类型,我们想要一个词汇. 我们也可以用refined做这样的事情: import eu.timepit.refined.api.Refined import eu.timepit.refined.numeric.Interval.ClosedOpen import shapeless.Witness class Vocab(values: Vector[String]) { type S <: Int type P = ClosedOpen[Witness.`0`.T,S] def size: S = values.size.asInstanceOf[S] def getId(value: String): Option[Refined[Int,P]] = values.indexOf(value) match { case -1 => None case i => Some(Refined.unsafeApply[Int,P](i)) } def getValue(id: Refined[Int,P]): String = values(id.value) } 即使S在编译时不知道,编译器仍然能够跟踪它给我们的ids在零和S之间的事实,所以我们不用担心失败的可能性我们返回值(当然,如果我们使用相同的词汇表达式). 我想要的是能够写这个: val x = 2 val vocab = new Vocab(Vector("foo","bar","qux")) eu.timepit.refined.refineV[vocab.P](x).map(vocab.getValue) 所以当用户真的需要时可以轻松查找任意的整数.这不编译,但是: scala> eu.timepit.refined.refineV[vocab.P](x).map(vocab.getValue) <console>:17: error: could not find implicit value for parameter v: eu.timepit.refined.api.Validate[Int,vocab.P] eu.timepit.refined.refineV[vocab.P](x).map(vocab.getValue) ^ 我可以通过提供S的Witness实例来编译它: scala> implicit val witVocabS: Witness.Aux[vocab.S] = Witness.mkWitness(vocab.size) witVocabS: shapeless.Witness.Aux[vocab.S] = shapeless.Witness$$anon$1@485aac3c scala> eu.timepit.refined.refineV[vocab.P](x).map(vocab.getValue) res1: scala.util.Either[String,String] = Right(qux) 当然,当该值超出范围时,它会失败(在运行时却是安全的) scala> val y = 3 y: Int = 3 scala> println(eu.timepit.refined.refineV[vocab.P](y).map(vocab.getValue)) Left(Right predicate of (!(3 < 0) && (3 < 3)) failed: Predicate failed: (3 < 3).) 我也可以将证人定义放在我的Vocab类中,然后导入vocab._,以便在需要时可以使用它,但是我真正想要的是能够提供精简V支持,无需额外的导入或定义. 我已经尝试过这样的各种各样的东西: object Vocab { implicit def witVocabS[V <: Vocab](implicit witV: Witness.Aux[V] ): Witness.Aux[V#S] = Witness.mkWitness(witV.value.size) } 但是,这仍然需要为每个vocab实例明确定义: scala> implicit val witVocabS: Witness.Aux[vocab.S] = Vocab.witVocabS witVocabS: shapeless.Witness.Aux[vocab.S] = shapeless.Witness$$anon$1@1bde5374 scala> eu.timepit.refined.refineV[vocab.P](x).map(vocab.getValue) res4: scala.util.Either[String,String] = Right(qux) 我知道我可以用一个宏来实现witVocabS,但是我觉得应该有更好的方式来做这样的事情,因为它似乎是一个非常合理的用例(而且我对这个完全不熟悉,所以完全是这样)可能我错过了一些明显的东西). 解决方法
结果是,如果我们通过使用shapeless.Witness为type.size分配单例类型的类型参数S具体:
import eu.timepit.refined.api.Refined import eu.timepit.refined.numeric.Interval.ClosedOpen import shapeless.Witness class Vocab(values: Vector[String]) { val sizeStable: Int = values.size val sizeWitness = Witness(sizeStable) type S = sizeWitness.T type P = ClosedOpen[Witness.`0`.T,S] def size: S = sizeWitness.value def getId(value: String): Option[Refined[Int,P]] = values.indexOf(value) match { case -1 => None case i => Some(Refined.unsafeApply[Int,P]): String = values(id.value) } 如果Scala允许单例类型的AnyVals,我们可以删除sizeWitness并定义类型S = sizeStable.type.这个限制在SIP-23 implementation年解除了. 使用refineV现在只需使用路径依赖类型vocab.P: scala> val vocab = new Vocab(Vector("foo","baz")) vocab: Vocab = Vocab@5fae6bb9 scala> refineV[vocab.P](2) res0: Either[String,eu.timepit.refined.api.Refined[Int,vocab.P]] = Right(2) scala> refineV[vocab.P](4) res1: Either[String,vocab.P]] = Left(Right predicate of (!(4 < 0) && (4 < 3)) failed: Predicate failed: (4 < 3).) scala> refineV[vocab.P](2).map(vocab.getValue) res2: scala.util.Either[String,String] = Right(baz) 这是因为编译器现在可以在Vocab实例范围之外找到隐含的Witness.Aux [vocab.S]: scala> val s = implicitly[shapeless.Witness.Aux[vocab.S]] s: shapeless.Witness.Aux[vocab.S] = shapeless.Witness$$anon$1@16cd7aa2 scala> s.value res2: s.T = 3 现在使用这个隐式实例来构造一个Validate [Int,vocab.P]实例,该实例是refineV用来决定Int是否是vocab的有效索引. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |