Scala:如何定义"通用"函数参数?

air*_*tyh 51 polymorphism haskell scala type-inference

我现在正在尝试学习Scala,在Haskell中有一些经验.对我来说奇怪的一件事是Scala中的所有函数参数都必须用类型注释 - 这是Haskell不需要的东西.为什么是这样?试着把它作为一个更具体的例子:add函数写成如下:

def add(x:Double, y:Double) = x + y
Run Code Online (Sandbox Code Playgroud)

但是,这只适用于双打(嗯,因为隐式类型转换,因此也可以工作).但是,如果要定义自己的类型来定义自己的+运算符,该怎么办?你会如何编写一个适用于定义+运算符的任何类型的add函数?

Wal*_*ang 68

Haskell使用Hindley-Milner类型推理算法,而Scala为了支持面向对象的方面,不得不放弃使用它.

为了轻松地为所有适用类型编写添加函数,您需要使用Scala 2.8.0:

Welcome to Scala version 2.8.0.r18189-b20090702020221 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_15).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import Numeric._
import Numeric._

scala> def add[A](x: A, y: A)(implicit numeric: Numeric[A]): A = 
     | numeric.plus(x, y)
add: [A](x: A,y: A)(implicit numeric: Numeric[A])A

scala> add(1, 2)
res0: Int = 3

scala> add(1.1, 2.2)
res1: Double = 3.3000000000000003
Run Code Online (Sandbox Code Playgroud)

  • @mtnygard否,"A"没有任何子类型约束.魔法发生在导入的scala.Numeric对象中.有许多隐式对象都实现了数字特征,而scalac在编译时会选择适当的对象.神奇的东西,真的.请查看api文档了解详细信息. (6认同)
  • 谢谢!这就像Haskell中的类型类一样.我还在http://www.scala-lang.org/node/114找到了*implicit*的一个很好的解释. (2认同)
  • 同样地,您可以使用上下文边界编写_same_代码:def add [A:Numeric](x:A,y:A)=隐式[Numeric [A]].plus(x,y)在许多情况下,您不会甚至需要隐式使用来恢复隐式传递的参数,因为它会被隐式传递给期望它的其他函数.例如:scala> def addTwice [A:Numeric](x:A,y:A)= add(add(x,y),y)addTwice:[A](x:A,y:A)(隐式证据$ 1 :数字[A])scala> addTwice(1,2)res2:Int = 5 (2认同)

air*_*tyh 18

为了巩固对我自己使用隐含的概念,我写了一个不需要scala 2.8但使用相同概念的例子.我认为这可能对某些人有所帮助.首先,定义一个泛型抽象类Addable:

scala> abstract class Addable[T]{
 |   def +(x: T, y: T): T
 | }
defined class Addable
Run Code Online (Sandbox Code Playgroud)

现在您可以像这样编写add函数:

scala> def add[T](x: T, y: T)(implicit addy: Addable[T]): T = 
 | addy.+(x, y)
add: [T](T,T)(implicit Addable[T])T
Run Code Online (Sandbox Code Playgroud)

这与Haskell中的类型类一样使用.然后为了实现特定类型的这个泛型类,你可以编写(这里的例子是Int,Double和String):

scala> implicit object IntAddable extends Addable[Int]{
 |   def +(x: Int, y: Int): Int = x + y
 | }
defined module IntAddable

scala> implicit object DoubleAddable extends Addable[Double]{
 |   def +(x: Double, y: Double): Double = x + y
 | }
defined module DoubleAddable

scala> implicit object StringAddable extends Addable[String]{
 |   def +(x: String, y: String): String = x concat y
 | }
defined module StringAddable
Run Code Online (Sandbox Code Playgroud)

此时,您可以使用以下三种类型调用add函数:

scala> add(1,2)
res0: Int = 3

scala> add(1.0, 2.0)
res1: Double = 3.0

scala> add("abc", "def")
res2: java.lang.String = abcdef
Run Code Online (Sandbox Code Playgroud)

当然不如Haskell那么好,它基本上可以为你做所有这些.但是,这就是权衡所在.