你好:我最近一直在学习 Scala(我的相关背景主要是 C++ 模板),我遇到了一些我目前对 Scala 不了解的东西,这让我发疯了。:(
(另外,这是我在 StackOverflow 上的第一篇文章,我注意到大多数非常棒的 Scala 人似乎都在闲逛,所以如果我对这个机制做了一些非常愚蠢的事情,我真的很抱歉。)
我的具体困惑与隐式参数绑定有关:我提出了一个特定情况,即隐式参数拒绝绑定,但具有看似相同语义的函数却可以。
现在,它当然可能是一个编译器错误,但鉴于我刚刚开始使用 Scala,我已经遇到某种严重错误的可能性非常小,以至于我期待有人解释我做错了什么。;P
我已经浏览了代码并对其进行了相当多的削减,以便提出一个不起作用的示例。不幸的是,这个例子仍然相当复杂,因为这个问题似乎只出现在泛化中。:(
1) 不按我预期的方式工作的简化代码
import HList.::
trait HApplyOps {
implicit def runNil
(input :HNil)
(context :Object)
:HNil
= {
HNil()
}
implicit def runAll[Input <:HList, Output <:HList]
(input :Int::Input)
(context :Object)
(implicit run :Input=>Object=>Output)
:Int::Output
= {
HCons(0, run(input.tail)(context))
}
def runAny[Input <:HList, Output <:HList]
(input :Input)
(context :Object)
(implicit run :Input=>Object=>Output)
:Output
= {
run(input)(context)
}
}
sealed trait HList
final case class HCons[Head, Tail <:HList]
(head :Head, tail :Tail)
extends HList
{
def ::[Value](value :Value) = HCons(value, this)
}
final case class HNil()
extends HList
{
def ::[Value](value :Value) = HCons(value, this)
}
object HList extends HApplyOps {
type ::[Head, Tail <:HList] = HCons[Head, Tail]
}
class Test {
def main(args :Array[String]) {
HList.runAny( HNil())(null) // yay! ;P
HList.runAny(0::HNil())(null) // fail :(
}
}
此代码使用 Scala 2.9.0.1 编译,返回以下错误:
broken1.scala:53: error: No implicit view available from HCons[Int,HNil] => (java.lang.Object) => Output.
HList.runAny(0::HNil())(null)
在这种情况下,我的期望runAll
是绑定run
到runAny
.
现在,如果我修改runAll
它,而不是直接获取它的两个参数,而是返回一个函数,该函数又接受这两个参数(我想尝试的技巧,因为我在其他人的代码中看到它),它可以工作:
2) 修改后的代码具有相同的运行时行为并且实际工作
implicit def runAll[Input <:HList, Output <:HList]
(implicit run :Input=>Object=>Output)
:Int::Input=>Object=>Int::Output
= {
input =>
context =>
HCons(0, run(input.tail)(context))
}
从本质上讲,我的问题是:为什么会这样?;( 我希望这两个函数具有相同的整体类型签名:
1: [Input <:HList, Output <:HList] (Int::Input)(Object):Int::Output
2: [Input <:Hlist, Output <:HList] :Int::Input=>Object=>Int::Output
如果它有助于理解问题,其他一些更改也“起作用”(尽管这些更改了函数的语义,因此不是可用的解决方案):
3)runAll
通过将 Output 替换为 HNil 仅对第二级进行硬编码
implicit def runAll[Input <:HList, Output <:HList]
(input :Int::Input)
(context :Object)
(implicit run :Input=>Object=>HNil)
:Int::HNil
= {
HCons(0, run(input.tail)(context))
}
4) 从隐式函数中删除上下文参数
trait HApplyOps {
implicit def runNil
(input :HNil)
:HNil
= {
HNil()
}
implicit def runAll[Input <:HList, Output <:HList]
(input :Int::Input)
(implicit run :Input=>Output)
:Int::Output
= {
HCons(0, run(input.tail))
}
def runAny[Input <:HList, Output <:HList]
(input :Input)
(context :Object)
(implicit run :Input=>Output)
:Output
= {
run(input)
}
}
任何人对此可能有的任何解释将不胜感激。:(
(目前,我最好的猜测是隐式参数相对于其他参数的顺序是我遗漏的关键因素,但我感到困惑的是:runAny
最后也有一个隐式参数,所以明显的“implicit def
不能很好地与尾随implicit
”对我来说没有意义。)