Reputation: 5923
I'm making a function that takes a lambda, and uses .tupled
on it if possible (arity 2+). For the compiler to allow using that, it needs to know if the lambda is really a Function2 (~ Function22). However, pattern-matching for Function2[Any,Any,Any]
means I'm left with a (Any, Any) => Any
rather than its original types. Not even knowing the arity isn't helping either. I tried matching like case f[A <: Any, B <: Any, C <: Any]: scala.Function2[A, B, C] => f.tupled
in an attempt to preserve types, but it doesn't really allow type parameters on a case.
The code:
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>
Bonus points if I won't need pattern-matching cases for each possible level of arity...
Upvotes: 1
Views: 79
Reputation: 55569
The answer is ugly, as you may have expected. Using a pattern-match on a function as a val won't work. When you start out with Any
you've already lost a ton of type information. And the standard library isn't really helping either, as there is no abstraction over arity of functions. That means that we can't even really use reflection to try to grab the type parameters, because we don't even know how many there are. You can figure out what FunctionN
you have, but not it's contained types, as they are already lost at this point.
Another possibility is to make tpl
a method and overload it for each 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>
It's not pretty, but it's at least type-safe. I don't think it would be too difficult to create a macro to generate the overloads 1-22.
Upvotes: 1