当使用Manifest(或TypeTag)隐式参数时,重载失败?

时间:2013-02-03 22:52:33

标签: scala

考虑以下(无用的)高阶函数,该函数试图访问其传递函数的参数的清单:

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;我不相信下面提出的存在类型替代方案会起作用。

Here's my actual code in case anyone's interested -- it's an abstraction over jsonCall in Lift to create AnonFunc JsExps from Scala functions.

谢谢!


更新:为什么我不能像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)
                     ^

如果我正在滥用类型系统,请立即停止我,但如果对原始重载问题没有满意的答案,这不会感觉像编译器错误吗?

1 个答案:

答案 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方法的参数名称。换句话说,无论函数对象是否包装方法,您始终会获得v1v2等等(通过示例查看Function2http://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]) {}