0

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

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)
                     ^

如果我正在滥用类型系统,请立即阻止我,但是如果对重载的原始问题没有令人满意的答案,这难道不是编译器错误吗?

4

1 回答 1

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]将如何在您发布的要点中获得任何帮助。查看代码,您显然使用TypeTagonly 来尝试提取参数名称。问题是您实际上正在获取apply函数对象方法的参数名称。换句话说,v1无论函数对象v2是否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]

对我来说似乎还可以。所以我建议只使用它。

请注意,对于existentials,这根本不会编译(所以我不知道你在哪里设法得到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]) {}
于 2013-02-04T00:16:28.423 回答