1

ラムダを取り、.tupled可能であればそれを使用する関数を作成しています(アリティ2+)。コンパイラがそれを使用できるようにするには、ラムダが本当に Function2 (~ Function22) であるかどうかを知る必要があります。ただし、 for のパターン マッチングは、元の型ではなくFunction2[Any,Any,Any]a が残っていることを意味します。(Any, Any) => Anyアリティを知らなくても役に立ちません。型を保持するためにマッチングを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>

可能性のある各レベルのパターン マッチング ケースが必要ない場合のボーナス ポイント...

4

1 に答える 1

1

ご想像のとおり、答えは醜いです。関数で val としてパターン マッチを使用しても機能しません。使い始めると、Anyすでに大量の型情報が失われています。また、関数のアリティに関する抽象化がないため、標準ライブラリも実際には役に立ちません。つまり、リフレクションを実際に使用して型パラメーターを取得しようとすることさえできないということです。何が含まれているかはわかりFunctionNますが、この時点で既に失われているため、含まれている型はわかりません。

もう 1 つの可能性は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 に答える