2016-02-27 102 views
8

我得到了一个奇怪的编译器错误,关于一个隐含的实际存在但无法找到的原因。所以我构建了一个能够再现神秘行为的小测试案例。为什么在某些情况下忽略高阶含义?

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 
} 

依赖于隐式转换的方法。它基本上是2x2矩阵。 id方法返回转换类型和drop方法内部使用转换并返回一些常量。正常方法在精确隐式转换类型上运行,并且方法对序列进行操作。

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] _) 

这是奇怪的事情。隐含符合所有要求。它被声明为implicit,所以它应该被编译器找到。并且在idSeq的情况下它实际上被发现。那么,为什么它在dropSeq的情况下被忽略?

回答

1

在你的情况idSeqdropSeq之间的唯一区别是返回类型:您已打在Scala编译器是值得信令斯卡拉社会某个角落的情况。

这就是说,你idSeq有错误签名:H#X并不意味着指定H型的X型,而是X对于已经由编译器解决了H(而不是一个任何实例,见丹尼尔·索布拉尔说明这里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,H <: HideAux[B], C](x : Seq[C])(implicit ev : Seq[C] => Seq[B]) : Int = { 
    println(ev(x)) 
    1 
    } 

此代码编译,并且还要注意的是,如果你正确地使用通用和类型类,你会不会因为动态行为是要被类型类本身提供需要两种不同的方法ididSeq

+2

如果您提交的问题是您所描述的“Scala编译器中的角落案例”,那么我更倾向于奖励您的赏金。我认为这是一些特例,但是你仍然没有提供足够的信息来知道这是否是预期的行为,或者它确实是编译器中的错误。这也将是很好的,如果你摆脱了悬挂'}'... – DaoWen

+0

你的两个函数具有相同的签名,除了返回类型,但你使用r1和r2的类型推断(因此返回类型不能用于隐式解析)。如果可以使用可用的类型信息解析r1的隐式表达式,则可以解析r2的隐式:假定该下拉工作并且dropSeq没有,那么问题可能与通用类型和类型投影有关。 – Edmondo1984

+0

使用'-Xlog-implicits'会产生一些有趣的错误。 –

相关问题