def weirdfunc(message: String, f: (Int, Int) => Int){
println(message + s" ${f(3,5)}")
}
我有上述功能。如何使函数f
对所有数值类型都是通用的?
答案 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