Scala将通用数字函数作为参数传递

时间:2017-06-29 20:15:00

标签: scala generics types parameters numeric

def weirdfunc(message: String, f: (Int, Int) => Int){
            println(message + s" ${f(3,5)}")
          }

我有上述功能。如何使函数f对所有数值类型都是通用的?

3 个答案:

答案 0 :(得分:3)

你想要的是一种排名较高的类型(具体来说,排名2)。 Haskell支持这些类型,Scala从Haskell获得了很多类型理论思想,但Scala尚未直接支持这一特定功能。

现在,有一点黑魔法,我们可以让Scala做你想做的事,但语法是......不漂亮。在Scala中,函数总是单形的,但是您希望将多态函数作为参数传递。我们无法做到这一点,但我们可以传递一个类似于多态函数的对象,它看起来和行为大多就像一个函数。这个对象会是什么样的?

trait NumFunc {
  def apply[A : Numeric](a: A, b: A): A
}

它只是一个定义多态apply的特征。现在我们可以定义你真正想要的功能。

def weirdfunc(message: String, f: NumFunc) = ???

正如我所提到的,这里的麻烦是语法非常残酷。要称呼它,我们不能再传递一个函数了。我们必须创建一个NumFunc并将其传入。基本上,从类型理论的角度来看,我们必须证明编译器我们的函数适用于所有数字A。例如,调用只接受整数并传递加法函数的简单weirdfunc非常简单。

weirdfunc("Some message", (_ + _))

然而,打电话给我们的"特别" weirdfunc适用于所有数字类型,我们必须写下这个混乱。

weirdfunc("Hi", new NumFunc {
  override def apply[A : Numeric](a: A, b: A): A = {
    import math.Numeric.Implicits._
    a + b
  }
})

我们无法通过隐式转换来隐藏它,因为正如我前面提到的,函数是单态的,因此任何函数类型的转换都将是单态的。

底线。可能吗?是。在可读性和可用性方面是否值得花费?可能不是。

答案 1 :(得分:2)

抱歉,你不能认同这一点。调用者可以设置泛型参数..而不是被调用函数..就像调用者传递函数参数一样 但是,您可以使用currying,以便传递Int类型的'f',然后传递不同的Int对。然后你可以传递Double类型的'f',然后传递不同的Double对。

  def weirdfunc[A](message: String, f: (A, A) => A)(x: A, y: A){
    println(message + s" ${f(x, y)}")
  }

  def g(x: Int, y: Int): Int = x * y
  val wierdfuncWithF = weirdfunc("hello", g) _
  wierdfuncWithF(3, 5)
  wierdfuncWithF(2, 3)

特别是你想要的东西无法完成,因为它会打破泛型规则。

答案 2 :(得分:2)

Scala有一个类型类,所以使用上下文绑定和标准库很容易实现。

def weirdfunc[T: Numeric](message: String, x: T, y: T, f: (T, T) => T) {
  println(message + s" ${f(x, y)}")
}

def test[T](a: T, b: T)(implicit ev: Numeric[T]): T = ev.plus(a, b)

weirdFunc[Int]("The sum is ", 3, 5, test)
// The sum is 8