Scala:如何定义“通用”函数参数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1252915/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scala: How to define "generic" function parameters?
提问by airportyh
I am trying to learn Scala now, with a little bit of experience in Haskell. One thing that stood out as odd to me is that all function parameters in Scala mustbe annotated with a type - something that Haskell does not require. Why is this? To try to put it as a more concrete example: an add function is written like this:
我现在正在尝试学习 Scala,在 Haskell 方面有一些经验。对我来说很奇怪的一件事是 Scala 中的所有函数参数都必须用类型进行注释——这是 Haskell 不需要的。为什么是这样?试着把它作为一个更具体的例子:一个 add 函数是这样写的:
def add(x:Double, y:Double) = x + y
But, this only works for doubles(well, ints work too because of the implicit type conversion). But what if you want to define your own type that defines its own +operator. How would you write an add function which works for any type that defines a +operator?
但是,这仅适用于双打(嗯,由于隐式类型转换,整数也可以工作)。但是如果你想定义自己的类型来定义它自己的+运算符怎么办。您将如何编写适用于定义+运算符的任何类型的 add 函数?
回答by Walter Chang
Haskell uses Hindley-Milner type inference algorithm whereas Scala, in order to support Object Oriented side of things, had to forgo using it for now.
Haskell 使用 Hindley-Milner 类型推理算法,而 Scala 为了支持面向对象的方面,现在不得不放弃使用它。
In order to write an add function for all applicable types easily, you will need to use Scala 2.8.0:
为了轻松地为所有适用类型编写 add 函数,您需要使用 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
回答by airportyh
In order to solidify the concept of using implicitfor myself, I wrote an example that does not require scala 2.8, but uses the same concept. I thought it might be helpful for some. First, you define an generic-abstract class Addable:
为了巩固我自己使用implicit的概念,我写了一个例子,不需要scala 2.8,但使用了相同的概念。我认为这可能对某些人有帮助。首先,定义一个泛型抽象类Addable:
scala> abstract class Addable[T]{
| def +(x: T, y: T): T
| }
defined class Addable
Now you can write the addfunction like this:
现在你可以像这样编写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
This is used like a type class in Haskell. Then to realizethis generic class for a specific type, you would write(examples here for Int, Double and String):
这就像 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
At this point you can call the addfunction with all three types:
此时,您可以使用所有三种类型调用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
Certainly not as nice as Haskell which will essentially do all of this for you. But, that's where the trade-off lies.
当然不如 Haskell 好,它基本上会为您完成所有这些。但是,这就是权衡所在。
回答by fxt
I think the reason Scala requires the type annotation on the parameters of a newly defined function comes from the fact that Scala uses a more local type inference analysis than that used in Haskell.
我认为 Scala 需要对新定义函数的参数进行类型注释的原因是因为 Scala 使用了比 Haskell 中使用的更局部的类型推断分析。
If all your classes mixed in a trait, say Addable[T], that declared the +operator, you could write your generic add function as:
如果您的所有类都混合在一个特征中,例如Addable[T],声明了+运算符,您可以将通用 add 函数编写为:
def add[T <: Addable[T]](x : T, y : T) = x + y
This restricts the add function to types T that implement the Addable trait.
这将 add 函数限制为实现 Addable trait 的类型 T。
Unfortunately, there is not such trait in the current Scala libraries. But you can see how it would be done by looking at a similar case, the Ordered[T]trait. This trait declares comparison operators and is mixed in by the RichInt, RichFloat, etc. classes. Then you can write a sort function that can take, for example, a List[T]where [T <: Ordered[T]]to sort a list of elements that mix in the ordered trait. Because of implicit type conversions like Floatto RichFloat, you can even use your sort function on lists of Int, or Floator Double.
不幸的是,当前的 Scala 库中没有这样的特性。但是您可以通过查看类似的案例Ordered[T]trait来了解它是如何完成的。此特征声明比较运算符和处于混合由RichInt,RichFloat等类。然后,您可以编写一个排序函数,例如,List[T]它[T <: Ordered[T]]可以对混合在有序特征中的元素列表进行排序。由于像Floatto这样的隐式类型转换RichFloat,您甚至可以在Int, orFloat或 的列表上使用排序函数Double。
As I said, unfortunately, there is no corresponding trait for the +operator. So, you would have to write out everything yourself. You would do the Addable[T] trait, create AddableInt, AddableFloat, etc., classes that extend Int, Float, etc. and mix in the Addable trait, and finally add implicit conversion functions to turn, for example, and Int into an AddableInt, so that the compiler can instantiate and use your add function with it.
正如我所说,不幸的是,+运营商没有相应的特征。因此,您必须自己写出所有内容。你会做 Addable[T] trait、create AddableInt、AddableFloat等,扩展 Int、Float 等的类并混合在 Addable trait 中,最后添加隐式转换函数以将,例如,和 Int 转换为AddableInt,所以编译器可以实例化并使用您的 add 函数。
回答by Daniel C. Sobral
Haskell uses the Hindley-Milnertype inference. This kind of type-inference is powerful, but limits the type system of the language. Supposedly, for instance, subclassing doesn't work well with H-M.
Haskell 使用Hindley-Milner类型推断。这种类型推断功能强大,但限制了语言的类型系统。例如,据说子类化不适用于 HM。
At any rate, Scala type system is too powerful for H-M, so a more limited kind of type inference must be used.
无论如何,Scala 类型系统对于 HM 来说太强大了,因此必须使用更有限的类型推断。
回答by Daniel C. Sobral
The function itself will be pretty straightforward:
该函数本身将非常简单:
def add(x: T, y: T): T = ...
Better yet, you can just overload the + method:
更好的是,您可以重载 + 方法:
def +(x: T, y: T): T = ...
There's a missing piece, though, which is the type parameter itself. As written, the method is missing its class. The most likely case is that you're calling the + method on an instance of T, passing it another instance of T. I did this recently, defining a trait that said, "an additive group consists of an add operation plus the means to invert an element"
但是,还有一个缺失的部分,那就是类型参数本身。正如所写,该方法缺少它的类。最可能的情况是你在 T 的一个实例上调用 + 方法,将另一个 T 实例传递给它。我最近做了这个,定义了一个特征说,“一个加法组由一个添加操作加上方法组成反转元素"
trait GroupAdditive[G] extends Structure[G] {
def +(that: G): G
def unary_- : G
}
Then, later, I define a Real class that knows how to add instances of itself (Field extends GroupAdditive):
然后,稍后,我定义了一个 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 = { ... }
...
}
That may be more than you really wanted to know right now, but it does show both how to define generic arguments and how to realize them.
这可能比您现在真正想知道的要多,但它确实展示了如何定义泛型参数以及如何实现它们。
Ultimately, the specific types aren't required, but the compiler does need to know at least the type bounds.
最终,不需要特定类型,但编译器至少需要知道类型界限。

