7

我想定义一个f接受另一个函数的函数g。我们需要g采取n双打(对于一些固定n的)并返回双打。函数调用f(g)应该返回n.

例如,f(Math.max) = 2由于 Math.sin 具有 type (Double, Double) => Double,并且f(Math.sin) = 1由于 Math.sin 具有 type Double => Double

如何f使用 Scala 泛型定义?

我尝试了几种形式都没有成功。例如:

def f[A <: Product](g: Product => Double) = {...}

这不起作用,因为我们无法n在编译时提取 的值,也不能将 限制A为仅包含Double值。

4

3 回答 3

7

有一种名为Magnet Pattern的图案,由Spray团队创建。它完全符合您的要求

于 2013-06-19T08:47:14.753 回答
6

这是我研究Shapeless的一个很好的借口,这是我一直想做的事情 :)

$ git clone git@github.com:milessabin/shapeless.git
...
$ cd shapeless

(1)

Shapeless 提供了一些对 arity 的抽象,尤其是异构列表 ( HList) 的表示。可以将任意数量的函数视为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)

现在我们仍然需要以某种方式提取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()

测试:

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)来完成这项工作。


并不是说这是在您的具体案例中执行此操作的最佳方法,但我认为这是一次有趣的探索。

于 2013-06-19T09:59:33.923 回答
2

可能最简单的解决方案是使用重载作为

def f(g: () => Double) = 0;
def f(g: (Double) => Double) = 1;
def f(g: (Double, Double) => Double) = 2;
def f(g: (Double, Double, Double) => Double) = 2;
// ...

println(f(Math.pow _));
println(f(Math.sin _));

(由于类型擦除,您无法在运行时检查函数参数/返回类型,因此我相信您无法创建一个完全通用的函数来满足您的要求。)

于 2013-06-19T09:17:38.167 回答