これは、私が常にある時点でやりたいと思っていたShapelessを調べるための良い言い訳でした:)
$ git clone git@github.com:milessabin/shapeless.git
...
$ cd shapeless
(1)
Shapeless は、アリティに対する抽象化、特に異種リスト ( HList
) としての表現を提供します。任意のアリティの関数は ( as 引数FnHList
を取る関数)と見なすことができます。HList
$ sbt shapeless-core/console
scala> import shapeless._
import shapeless._
scala> def isFunction[A](fun: A)(implicit fnh: FnHLister[A]) {}
isFunction: [A](fun: A)(implicit fnh: shapeless.FnHLister[A])Unit
scala> isFunction(math.sqrt _)
scala> isFunction(math.random _)
(2)
次に、関数が次を返すことを要求しましょうDouble
:
scala> def isFunReturningDouble[A](fun: A)(implicit fnh: FnHLister[A] { type Result = Double }) {}
isFunReturningDouble: [A](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double})Unit
scala> isFunReturningDouble(math.sqrt _)
scala> isFunReturningDouble(math.signum _)
<console>:12: error: could not find implicit value for parameter fnh: shapeless.FnHLister[Int => Int]{type Result = Double}
isFunReturningDouble(math.signum _)
^
(3)
LUBConstraint
型クラスは、引数リストの上限を確認できます。
scala> def isValidFun[A, B <: HList](fun: A)(implicit fnh: FnHLister[A] { type Result = Double; type Args = B }, lub: LUBConstraint[B, Double]) {}
isValidFun: [A, B <: shapeless.HList](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double; type Args = B}, implicit lub: shapeless.LUBConstraint[B,Double])Unit
scala> isValidFun(math.random _)
scala> isValidFun((i: Int) => i.toDouble)
<console>:12: error: could not find implicit value for parameter lub: shapeless.LUBConstraint[B,Double]
isValidFun((i: Int) => i.toDouble)
^
(4)
ここで、どうにかしてアリティを抽出する必要があります。タイプ レベルでは、Length
これは に提供されHList
ます。ランタイム値を取得するには、別の型クラスToInt
が必要です。
最終的な関数は次のとおりです。
import shapeless._
def doubleFunArity[A, B <: HList, C <: Nat](fun: A)(implicit
fnh: FnHLister[A] { type Result = Double; type Args = B },
lub: LUBConstraint[B, Double],
len: Length[B] { type Out = C },
res: ToInt[C]
): Int = res()
テスト:
scala> doubleFunArity(math.sqrt _)
res15: Int = 1
scala> doubleFunArity(math.random _)
res16: Int = 0
scala> val g: (Double, Double) => Double = math.max _
g: (Double, Double) => Double = <function2>
scala> doubleFunArity(g)
res17: Int = 2
残念ながら、多くのmath
操作がオーバーロードされていることに注意してください。強力な型制約がなければ、Scala はDouble
自動的にバージョンを提供しませんがInt
、何らかの理由でバージョンを使用します。
scala> math.max _
res18: (Int, Int) => Int = <function2>
したがって、これを機能させるには間接化が必要math.max _: ((Double, Double) => Double)
です。
これがあなたの具体的なケースで最善の方法であるとは言いませんが、楽しい探索だったと思います.