考虑以下(无用的)高阶函数,它试图访问其传递函数参数的清单:
def foo[T <: (P1) => Unit, P1](f: T)(implicit p1: Manifest[P1]) {}
这是一个编译良好的示例:
def theFunc(i: Int) {}
def foo[T <: (P1) => Unit, P1](f: T)(implicit p1: Manifest[P1]) {}
foo(theFunc _)
假设我们要添加一个支持两个参数函数的重载:
def theFunc(i: Int) {}
def foo[T <: (P1) => Unit, P1](f: T)(implicit p1: Manifest[P1]) {}
def foo[T <: (P1, P2) => Unit, P1, P2](f: T)(implicit p1: Manifest[P1], p2: Manifest[P2]) {}
foo(theFunc _)
..编译器说没那么快:
overloaded method value foo with alternatives:
[error] [T(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo) <: (P1, P2) => Unit, P1, P2](f: T(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo))(implicit p1: Manifest[P1], implicit p2: Manifest[P2])Unit <and>
[error] [T(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo) <: P1 => Unit, P1](f: T(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo)(in method foo))(implicit p1: Manifest[P1])Unit
[error] cannot be applied to (Int => Unit)
[error] foo(theFunc _)
[error] ^
[error] one error found
好的,如果我们去掉 Manifest 隐式参数呢?
def theFunc(i: Int) {}
def foo[T <: (P1) => Unit, P1](f: T) {}
def foo[T <: (P1, P2) => Unit, P1, P2](f: T) {}
foo(theFunc _)
再次编译正常!
使用一些不同类型的隐式参数怎么样?
def theFunc(i: Int) {}
def foo[T <: (P1) => Unit, P1](f: T)(implicit i: Int) {}
def foo[T <: (P1, P2) => Unit, P1, P2](f: T)(implicit i: Int, s: String) {}
implicit val i = 3
implicit val s = "hi"
foo(theFunc _)
也编译得很好。
有人知道这里发生了什么吗?我正在运行 Scala 2.10.0。我尝试将 TypeTags 交换为 Manifests 并得到相同的错误,所以我猜测它特定于编译器提供的隐式。
请注意,对于我的真实用例,我很确定将 T 定义为类型参数是必需的,因为我需要它的 TypeTag;我不相信下面提出的存在类型替代方案会起作用。
如果有人感兴趣,这是我的实际代码——它是 Lift 中 jsonCall 的抽象,用于从 Scala 函数创建 AnonFunc JsExps。
谢谢!
更新:为什么我不能像 Régis Jean-Gilles 建议的那样使用存在类型?
在我上面链接的真实用例中,我还需要传递函数的 TypeTag。我传递的不是匿名函数,而是扩展 Function2 的对象:
object MyFunc extends ((Int, String) => JsCmd) {
def apply(myInt: Int, myString: String) = JsCmds.Noop
}
这使我可以通过 TypeTag 读取函数的参数名称(请参阅要点)。
练习一些关于存在类型的天真,我尝试了这个:
object MyFunc extends ((Int, String) => Unit) {
def apply(myInt: Int, myString: String) {}
}
def foo[P1, P2, T](f: T forSome { type T <: (P1, P2) => Unit})(implicit tag: TypeTag[T], p1: Manifest[P1], p2: Manifest[P2]) = tag
foo(MyFunc)
这当然会产生:
<console>:12: error: type parameter not specified
foo(MyFunc)
^
如果我正在滥用类型系统,请立即阻止我,但是如果对重载的原始问题没有令人满意的答案,这难道不是编译器错误吗?