scala – 为什么在某些情况下会忽略高阶隐含?
我得到一个奇怪的编译器错误,关于一个隐含的实际存在但由于某种原因无法找到.所以我构建了一个小型测试用例,可以重现神秘的行为.
trait Hide { type T } object HideString extends Hide { override type T = String } object HideBool extends Hide { override type T = Boolean } 简单类型用作隐式转换的明确目标. def id[H <: Hide,C](x : C)(implicit ev : C => H#T) : H#T = ev(x) def drop[H <: Hide,C](x : C)(implicit ev : C => H#T) : Int = { println(ev(x)) 1 } def idSeq[H <: Hide,C](x : Seq[C])(implicit ev : Seq[C] => Seq[H#T]) : Seq[H#T] = ev(x) def dropSeq[H <: Hide,C](x : Seq[C])(implicit ev : Seq[C] => Seq[H#T]) : Int = { println(ev(x)) 1 } 依赖隐式转换的方法.它基本上是2×2矩阵. id方法返回转换类型和drop方法在内部使用转换并返回一些常量.普通方法在精确隐式转换类型上运行,Seq方法在序列上运行. implicit def exString(x : String) : HideString.type#T = x implicit def highString[F[_]](x : F[String]) : F[HideString.type#T] = x 在隐式转换之上,highString是使用高阶类型定义的. val s1 = id("sdf") val s2 = drop("aero") val r1 = idSeq(Seq("a","bc")) val r2 = dropSeq(Seq("i","IO")) 尝试实际使用转换会给我带来错误: ImplicitResolution.scala:98: error: No implicit view available from Seq[String] => Seq[test.implicits.HighReduction.Hide#T]. val r2 = dropSeq(Seq("i","IO")) 这可以归纳为以下矩阵: | | id | drop | |--------+------+------| | normal | pass | pass | | seq | pass | fail | 如果我对dropSeq方法使用精确定义的隐式转换,则通常会找到: implicit def seqBool(x : Seq[Boolean]) : Seq[HideBool.type#T] = x val a1 = idSeq(Seq(true,false)) val a2 = dropSeq(Seq(false,true)) 此外,如果我明确指定隐式参数dropSeq开始工作: val r2i = dropSeq(Seq("i","IO"))(highString[Seq] _) 这是最奇怪的事情. highString隐式适合所有要求.并且它被声明为隐式,因此它应该由编译器找到.在idSeq的情况下,实际上找到了它.那么,为什么在dropSeq案例中忽略它呢? 解决方法
在你的情况下,idSeq和dropSeq之间的唯一区别是返回类型:你已经在Scala编译器中找到了一些值得向Scala社区发出信号的情况.
也就是说,你的idSeq有错误的签名:H#X并不代表指定H类型的X类型,而是指任何H实例的X(不是编译器已解析的那个,请参阅Daniel Sobral在此解释What does the `#` operator mean in Scala?) 您可能想要做的是在H和结果类型之间建立关系,如果您引入类型别名以获得更可读的签名,则更容易: object Hide { type HideAux[X] = Hide { type T = X} } 然后,您可以像这样重写代码: def idSeq[B,H <: HideAux[B],C](x : Seq[C])(implicit ev : Seq[C] => Seq[B]) : Seq[B] = ev(x) def dropSeq[B,C](x : Seq[C])(implicit ev : Seq[C] => Seq[B]) : Int = { println(ev(x)) 1 } 这段代码编译,并注意到如果你正确使用泛型和类型类,你将不需要两个不同的方法id和idSeq,因为动态行为将由类型类本身提供. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |