1

我正在制作一个带有 lambda 的函数,并.tupled在可能的情况下使用它(arity 2+)。为了让编译器允许使用它,它需要知道 lambda 是否真的是 Function2(~ Function22)。但是,模式匹配Function2[Any,Any,Any]意味着我只剩下 a(Any, Any) => Any而不是它的原始类型。甚至不知道arity也无济于事。我尝试进行匹配case f[A <: Any, B <: Any, C <: Any]: scala.Function2[A, B, C] => f.tupled以尝试保留类型,但它实际上并不允许在案例中使用类型参数。

编码:

val add = (a: Int, b: Int) => a + b
add: (Int, Int) => Int = <function2>

val tpl = (fn: Any) => {
  fn match {
    case f: Function0[Any] => f
    case f: Function1[Any,Any] => f
    case f: Function2[Any,Any,Any] => f.tupled
//  case f: Function3[Any,Any,Any,Any] => f.tupled
//  ...
//  case _ => { throw new Exception("huh") }
  }
}

// actual result:
tpl(add)
res0: Any = <function1>

// desired result is like this one:
scala> add.tupled
res3: ((Int, Int)) => Int = <function1>

如果我不需要每个可能的arity级别的模式匹配案例,那么加分......

4

1 回答 1

1

正如您所预料的那样,答案很丑陋。在函数上使用模式匹配作为 val 是行不通的。当您开始使用时,Any您已经丢失了大量类型信息。标准库也没有真正的帮助,因为没有对函数数量的抽象。这意味着我们甚至不能真正使用反射来尝试获取类型参数,因为我们甚至不知道有多少。你可以弄清楚FunctionN你有什么,但不是它包含的类型,因为此时它们已经丢失了。

另一种可能性是创建tpl一个方法并为每个方法重载它FunctionN

def tpl[A](f: Function0[A]): Function0[A] = f
def tpl[A, R](f: Function1[A, R]): Function1[A, R] = f
def tpl[A1, A2, R](f: Function2[A1, A2, R]): Function1[(A1, A2), R] = f.tupled
def tpl[A1, A2, A3, R](f: Function3[A1, A2, A3, R]): Function1[(A1, A2, A3), R] = f.tupled
// ... and so on

scala> val add = (a: Int, b: Int) => a + b
add: (Int, Int) => Int = <function2>

scala> tpl(add)
res0: ((Int, Int)) => Int = <function1>

它不漂亮,但至少是类型安全的。我认为创建一个宏来生成重载 1-22 并不难。

于 2015-09-07T14:50:19.643 回答