繁体   English   中英

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

[英]Scala: How to define “generic” function parameters?

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

def add(x:Double, y:Double) = x + y

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

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

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

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

现在您可以像这样编写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

这与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

此时,您可以使用以下三种类型调用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

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

我认为Scala需要对新定义函数的参数进行类型注释的原因来自Scala使用比Haskell中使用的更局部类型推断分析的事实。

如果你的所有类都混合了一个特性,比如Addable[T] ,它声明了+运算符,你可以将你的泛型add函数编写为:

def add[T <: Addable[T]](x : T, y : T) = x + y

这将add函数限制为实现Addable trait的类型T.

不幸的是,当前的Scala库中没有这样的特性。 但你可以通过查看类似的案例Ordered[T]特征来看看它是如何完成的。 此特征声明比较运算符,并由RichIntRichFloat等类混合。 然后你可以编写一个sort函数,它可以采用例如List[T] ,其中[T <: Ordered[T]]对在有序特征中混合的元素列表进行排序。 由于隐式类型转换(如Float to RichFloat ,您甚至可以在IntFloatDouble列表上使用sort函数。

正如我所说,遗憾的是, +运算符没有相应的特征。 所以,你必须自己写出一切。 您可以执行Addable [T]特性,创建AddableIntAddableFloat等,扩展Int,Float等的类,并在Addable特性中混合,最后添加隐式转换函数以转换为例如Int和Int AddableInt ,以便编译器可以实例化并使用你的add函数。

Haskell使用Hindley-Milner类型推断。 这种类型推断功能强大,但限制了语言的类型系统。 据说,例如,子类化对HM不起作用。

无论如何,Scala类型系统对HM来说太强大了,因此必须使用更有限的类型推断。

函数本身非常简单:

def add(x: T, y: T): T = ...

更好的是,你可以重载+方法:

def +(x: T, y: T): T = ...

但是,有一个缺失的部分,这是类型参数本身。 如上所述,该方法缺少其类。 最可能的情况是你在T的一个实例上调用+方法,传递另一个T的实例。我最近做了这个,定义了一个特征,“添加组包含一个添加操作加上反转元素“

trait GroupAdditive[G] extends Structure[G] {
  def +(that: G): G
  def unary_- : G
}

然后,我稍后定义一个知道如何添加自身实例的Real类(Field extends GroupAdditive):

class Real private (s: LargeInteger, err: LargeInteger, exp: Int) extends Number[Real] with Field[Real] with Ordered[Real] {
  ...

  def +(that: Real): Real = { ... }

  ...
}

这可能比你现在真正想知道的更多,但它确实展示了如何定义泛型参数以及如何实现它们。

最终,不需要特定类型,但编译器确实需要至少知道类型边界。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM