我想定义一个f带另一个函数的函数g.我们需要g采取n双打(对于一些固定的n)并返回双.函数调用f(g)应该返回特定的值n.
例如,f(Math.max) = 2由于Math.sin具有类型(Double, Double) => Double,f(Math.sin) = 1因为Math.sin具有类型Double => Double.
如何f使用Scala泛型定义?
我尝试了几种形式但没有成功.例如:
def f[A <: Product](g: Product => Double) = {...}
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为我们无法n在编译时提取值,并且不能约束A仅包含Double值.
这是我研究Shapeless的好借口,这是我一直想做的事情:)
$ git clone git@github.com:milessabin/shapeless.git
...
$ cd shapeless
Run Code Online (Sandbox Code Playgroud)
(1)
Shapeless提供了一些关于arity的抽象,尤其是表示为异构列表(HList)。可以将任意arity函数视为FnHList(以HListas为参数的函数)。
$ 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 _)
Run Code Online (Sandbox Code Playgroud)
(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 _)
^
Run Code Online (Sandbox Code Playgroud)
(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)
^
Run Code Online (Sandbox Code Playgroud)
(4)
现在我们仍然需要以某种方式提取arity。在类型级别,将为此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()
Run Code Online (Sandbox Code Playgroud)
测试:
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
Run Code Online (Sandbox Code Playgroud)
请注意,不幸的math是,许多操作都被重载,并且没有强类型约束,Scala不会Double自动为您提供版本,但是Int出于某些原因会使用该版本:
scala> math.max _
res18: (Int, Int) => Int = <function2>
Run Code Online (Sandbox Code Playgroud)
所以我需要间接方式math.max _: ((Double, Double) => Double)来使这项工作。
并不是说这是在具体情况下做到这一点的最佳方法,但我认为这是一个有趣的探索。
| 归档时间: |
|
| 查看次数: |
975 次 |
| 最近记录: |