考虑以下(无用的)高阶函数,该函数试图访问其传递函数的参数的清单:
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 for Manifests并得到相同的错误,所以我猜它是特定于编译器提供的implicits。
请注意,我非常确定将T定义为类型参数是我的真实用例所必需的,因为我需要它的TypeTag;我不相信下面提出的存在类型替代方案会起作用。
谢谢!
更新:为什么我不能像RégisJean-Gilles推荐的那样使用存在类型?
在我上面链接的实际用例中,我还需要传递函数的TypeTag。我传递的不是匿名函数,而是扩展Function2的对象:
object MyFunc extends ((Int, String) => JsCmd) {
def apply(myInt: Int, myString: String) = JsCmds.Noop
}
这允许我通过TypeTag(see the gist)读取函数的参数名称。
对存在主义类型进行了一些天真的尝试,我试过这个:
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)
^
如果我正在滥用类型系统,请立即停止我,但如果对原始重载问题没有满意的答案,这不会感觉像编译器错误吗?
答案 0 :(得分:1)
第一个问题是为什么你有这么复杂的类型参数?看看你的例子,你似乎可以很好地转变这些定义:
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]) {}
更简单的定义:
def foo[P1](f: P1 => Unit)(implicit p1: Manifest[P1]) {}
def foo[P1, P2](f: (P1, P2) => Unit)(implicit p1: Manifest[P1], p2: Manifest[P2]) {}
对于后者,对foo
的调用编译得很好。
如果由于某种原因你真的需要类型T
(例如你的实际代码定义了扩展函数的类型和你的foo
方法返回的东西取决于确切的类型的函数),你可以依赖存在类型:
def foo[P1](f: T forSome { type T <: (P1) => Unit})(implicit p1: Manifest[P1]) {}
def foo[P1, P2](f: T forSome { type T <: (P1, P2) => Unit})(implicit p1: Manifest[P1], p2: Manifest[P2]) {}
至于为什么你首先得到一个错误,我老实说没有确凿的解释。
更新:好的,所以从下面的评论看来,我的直觉似乎很好:你过度简化了你的例子,因为你实际上需要类型参数T
我说似乎,因为老实说,我看不出你的TypeTag[T]
如何在你发布的要点中找到你的位置。查看代码,您显然只使用TypeTag
来尝试提取参数名称。问题是你实际上正在获取函数对象的apply
方法的参数名称。换句话说,无论函数对象是否包装方法,您始终会获得v1
,v2
等等(通过示例查看Function2
:http://www.scala-lang.org/api/current/index.html#scala.Function2的定义)使用不同的参数名称。
那就是说,我从现在开始假设您需要为函数类型获取TypeTag
。
我上面给出的第一个解决方案(根本没有T
参数的解决方案)实际上没有问题,所以你可能想再试一次。插图:
def foo[P1](f: P1 => Unit)(implicit tag: TypeTag[P1 => Unit], p1: Manifest[P1]) { println( "TypeTag: " + tag ) }
def foo[P1, P2](f: (P1, P2) => Unit)(implicit tag: TypeTag[(P1, P2) => Unit], p1: Manifest[P1], p2: Manifest[P2]) { println( "TypeTag: " + tag ) }
foo(theFunc _)
结果:
TypeTag: TypeTag[Int => Unit]
对我来说似乎没问题。所以我建议只使用它。
请注意,对于存在性,这根本不会编译(所以我不知道你设法获得Any
的位置):
def foo[P1](f: T forSome { type T <: (P1) => Unit})(implicit tag: TypeTag[T], p1: Manifest[P1]) {}
def foo[P1, P2](f: T forSome { type T <: (P1, P2) => Unit})(implicit tag: TypeTag[T], p1: Manifest[P1], p2: Manifest[P2]) {}
<console>:15: error: not found: type T
def foo[P1, P2](f: T forSome { type T <: (P1, P2) => Unit})(implicit tag: TypeTag[T], p1: Manifest[P1], p2: Manifest[P2]) {}
^
<console>:14: error: not found: type T
def foo[P1](f: T forSome { type T <: (P1) => Unit})(implicit tag: TypeTag[T], p1: Manifest[P1]) {}