最近,我在Scala中使用了类型级编程,并发现了以下内容:
trait NextPage[Curr, Next] {
def next : Next
}
class Foo
class Bar
class X(val year : Int)
object X {
implicit def xToNextPage[Y](x : X) : NextPage[X, Y] =
if (x.year == 2010) {
new X(x.year) with NextPage[X, Bar] {
def next = new Bar
}
}
else {
new X(x.year) with NextPage[X, Foo] {
def next = new Foo
}
}
}
val x = new X(2010)
val y = x.next //BOOM!
最后一行无限期冻结了解释器。有什么奇怪的,如果你只改变一行代码:
implicit def xToNextPage[Y](x : X) : NextPage[X, Y] =
到那个
implicit def xToNextPage(x : X) : NextPage[X, _] =
计算将成功执行(但结果类型将丢失,当然)。
你知道为什么会这样吗?我相信,它以某种方式与类型推理有关......
答案 0 :(得分:2)
原因是由于隐式转换,它处于无限递归状态。从implicit
移除xToNextPage
关键字,它会显示错误:
<console>:29: error: type mismatch;
found : X with NextPage[X,Bar]
required: NextPage[X,Y]
new X(x.year) with NextPage[X, Bar] {
显然,您的函数声明表明您正在返回NextPage[X, Y]
,但实际上您返回NextPage[X,Any]
。
它是递归的,因为当标记为implicit
时,因为函数返回类型是[X, Y]
。但是因为您要返回[X,Any]
,它会再次调用隐式函数xToNextPage
来尝试转换它。
解决方案:将声明更改为:
trait NextPage[Curr, +Next] {
def next : Next
}
implicit def xToNextPage[Y](x : X) : NextPage[X, Any]
答案 1 :(得分:0)
我不确定你在这里要做什么。我对Scala中的类型级编程没有任何经验,但这对我来说看起来不像正确的类型级编程。您在Y
中使用类型参数xToNextPage[Y](x : X)
尝试实现的目标是什么? Next[X, Y]
唯一可能的值是Next[X, AnyRef]
,因为Foo
和Bar
的最小共同祖先是AnyRef
。摆脱Y
会给你唯一正确答案:
trait NextPage[Curr, Next] {
def next : Next
}
class Foo
class Bar
class X(val year : Int)
object X {
implicit def xToNextPage(x : X) =
if (x.year == 2010) {
new X(x.year) with NextPage[X, Bar] {
def next = new Bar
}
}
else {
new X(x.year) with NextPage[X, Foo] {
def next = new Foo
}
}
}
val x = new X(2010)
val y = x.next // => y: Object = Bar@6704e2a0
我不认为这真的能回答你的问题,但我认为这可能会有所帮助。我很想知道其他人是否可以提出一个实际推断y: Bar
的解决方案,但我认为这超出了类型系统的限制。