类型别名函数类型的隐式转换类无法在Scala中编译

Pri*_*NAI 10 scala implicit-conversion

在scala中,以下代码正确编译:

class a {}
class b {}

object Main {

  implicit class Conv[f, t](val v: f ? t) extends AnyVal {
    def conv = v
  }

  def main(args: Array[String]) {
    val m = (a: a) ? new b
    m.conv
  }
}
Run Code Online (Sandbox Code Playgroud)

但由于某种原因,以下无法编译:

class a {}
class b {}

object Main {
  type V[f, t] = f ? t

  implicit class Conv[f, t](val v: V[f, t]) extends AnyVal {
    def conv = v
  }

  def main(args: Array[String]) {
    val m = (a: a) ? new b
    m.conv
  }
}
Run Code Online (Sandbox Code Playgroud)

以下消息:

value conv is not a member of a => b
    m.conv
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

编辑:是的,甚至还有一个错误

  val m: V[a,b] = new V[a,b] { def apply(a: a) = new b }
Run Code Online (Sandbox Code Playgroud)

Mik*_*rry 4

在第一个示例中,val v: f => t被推断为类型签名[-A, +B],因为它是一个参数的 Function 的简写。Function1具有类型签名,Function1[-A, +B]. 因此,参数是逆变的A,参数是协变的B

\n\n

(a: a) => new b然后,代码后面的lambda 函数将其类型推断为从 a 到 b 的函数。因此,类型签名是相同的并且隐式解析有效。

\n\n

在第二个示例中,类型V[f, t] = f => t和从中创建的参数:val v: V[f, t],将其类型显式指定为V[f, t]。该函数f => t仍然是[-A, +B],但是您在类型签名中明确限制类型为 Invariant ,因此该类型V在两个类型参数中都是 Invariant 。

\n\n

稍后,当您声明:val m = (a: a) => new bthis 的类型签名仍与[-A, +B]第一个示例中相同。隐式解析无法工作,因为 val 在其第一个类型参数中是逆变的,但类型V在其第一个类型参数中是不变的。

\n\n

将 V 的类型签名更改为V[-f, +t]orV[-f, t]可以解决此问题,并且隐式解析再次起作用。

\n\n

这确实提出了一个问题:为什么第二个类型参数的协方差不是隐式解析的问题,而第一个类型参数的逆变却是问题。我玩了一下并做了一些研究。我发现了一些有趣的链接,这表明隐式解析肯定存在一些限制/问题,特别是在逆变方面。

\n\n\n\n

我必须请了解 Scala 编译器内部结构和解析机制的人来了解更多细节,但这似乎与逆变上下文中隐式解析的一些限制相冲突。

\n\n

对于你的第三个例子,我认为你的意思是:

\n\n
class a {}\nclass b {}\n\nobject Main {\n  type V[f, t] = f => t\n\n  implicit class Conv[f, t](val v: V[f, t]) extends AnyVal {\n    def conv = v\n  }\n\n  def main(args: Array[String]) {\n    val m: V[a,b] = new V[a,b] { def apply(a: a) = new b }\n    m.conv // does not compile\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是一个有趣的问题,我认为这是一个略有不同的原因。类型别名在差异方面受到限制,但允许差异更严格。我不能确定这里发生了什么,但这里有一个与类型别名和方差相关的有趣的堆栈溢出问题

\n\n

考虑到隐式解析的复杂性,再加上类型声明方差与隐含 Function1 方差的附加因素,我怀疑编译器无法解决该特定场景中的任何问题。

\n\n

更改为:

\n\n
implicit class Conv(val v: V[_, _]) extends AnyVal {\n  def conv = v\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

意味着它适用于所有场景,因为您基本上是在对编译器说,出于 Conv 隐式类的目的,您不关心 V 上类型参数的方差。

\n\n

例如:以下也有效

\n\n
class a {}\nclass b {}\n\nobject Main {\n  type V[f, t] = f \xe2\x87\x92 t\n\n  implicit class Conv(val v: V[_, _]) extends AnyVal {\n    def conv = v\n  }\n\n  def main(args: Array[String]) {\n    val m = (a: a) \xe2\x87\x92 new b\n    m.conv\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n