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)
在第一个示例中,val v: f => t
被推断为类型签名[-A, +B]
,因为它是一个参数的 Function 的简写。Function1
具有类型签名,Function1[-A, +B]
. 因此,参数是逆变的A
,参数是协变的B
。
(a: a) => new b
然后,代码后面的lambda 函数将其类型推断为从 a 到 b 的函数。因此,类型签名是相同的并且隐式解析有效。
在第二个示例中,类型V[f, t] = f => t
和从中创建的参数:val v: V[f, t]
,将其类型显式指定为V[f, t]
。该函数f => t
仍然是[-A, +B]
,但是您在类型签名中明确限制类型为 Invariant ,因此该类型V
在两个类型参数中都是 Invariant 。
稍后,当您声明:val m = (a: a) => new b
this 的类型签名仍与[-A, +B]
第一个示例中相同。隐式解析无法工作,因为 val 在其第一个类型参数中是逆变的,但类型V
在其第一个类型参数中是不变的。
将 V 的类型签名更改为V[-f, +t]
orV[-f, t]
可以解决此问题,并且隐式解析再次起作用。
这确实提出了一个问题:为什么第二个类型参数的协方差不是隐式解析的问题,而第一个类型参数的逆变却是问题。我玩了一下并做了一些研究。我发现了一些有趣的链接,这表明隐式解析肯定存在一些限制/问题,特别是在逆变方面。
\n\n\n\n我必须请了解 Scala 编译器内部结构和解析机制的人来了解更多细节,但这似乎与逆变上下文中隐式解析的一些限制相冲突。
\n\n对于你的第三个例子,我认为你的意思是:
\n\nclass 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\nimplicit 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\nclass 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
归档时间: |
|
查看次数: |
176 次 |
最近记录: |