在Scala中,为什么不能实现这样的琐碎的泛型函数? [英] In Scala, why can't I implement a trivial generic function like this?

查看:77
本文介绍了在Scala中,为什么不能实现这样的琐碎的泛型函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个称为"double"的通用函数,其行为类似于这样,并且可以通过def +(x:T):T方法应用于任何类型:

I want a generic function called "double", which behaves like this and could be applied to any type with def +(x:T):T method:

double("A")
> "AA"
double(1)
> 2
double(0.2)
> 0.4

所以我这样写这个函数:

So I write this function like this:

def double[T](x:T):T = { x+x }

但是当我在REPL中运行它时,scala对此表示抱怨:

But when I run it in REPL, scala compains about it:

scala> def double[T](x:T):T = { x+x }
<console>:7: error: type mismatch;
 found   : T
 required: String
       def double[T](x:T):T = { x+x }
                                  ^

我认为结构类型可能是一种方法来实现鸭子类型,我尝试了类似的方法,但是它也不起作用:

I think structural type may be an approach to implement duck typing, and I tried something like this, but it doesn't work either:

def double[T <: { def +(x:T):T }](x:T):T = { x + x }
def double[T <: { def +[U<:T](x:U):U}](x:T) = { x + x }

有人对此有想法吗?谢谢!

Does anyone have ideas about this? Thanks!

我在Haskell中发现,类似的函数可以这样写:

I found in Haskell, the similar function can be written like this:

double x = x + x

只是想知道为什么我不能在Scala中做到这一点...

just wondering why I can't do this in Scala...

推荐答案

并非每个类型T都有+方法,因此该方法不起作用.奇怪的错误消息来自编译器,将第一个x视为String,因为每种类型都有一个toString方法,这是它可以将通用T视为具有+的唯一方法.但是随后T被传递给+而不是String,并且它需要第二次隐式转换才能使它起作用-即使我们这样做了,它也会返回String而不是T.

Not every type T has a + method, so that can't work. The strange error message comes from the compiler treating the first x as a String, because every type has a toString method, and that's the only way it can see a generic T as having +. But then T is being passed to +, instead of String, and it would require a second implicit conversion to allow this to work--and even if we did that, it would return String instead of T.

问题是我们需要一种方法来提供证据,证明T具有+操作.据我所知,标准库中没有任何东西可以做到这一点,但是我们可以创建一个类型类,以提供类型可以加倍"的证据.

The problem is that we need a way to provide evidence that T has a + operation. There isn't anything in the standard library that does exactly this, to my knowledge, but we can create a type class that would provide the evidence that a type can be "doubled".

trait CanDouble[A] {
    def double(a: A): A
}

// Create some instances for types we know, like String, or numeric types
implicit val StringDouble: CanDouble[String] = new CanDouble[String] {
    def double(a: String): String = a + a
}

// Uses the Numeric type class to create a CanDouble for all Numeric types
implicit def numericDouble[A: Numeric]: CanDouble[A] = {
    new CanDouble[A] {
         def double(a: A): A = implicitly[Numeric[A]].plus(a, a)
    }
}

现在,我们可以定义double方法,该方法需要类型为CanDouble类型的证据.

Now we can define the double method that requires a evidence of the type class CanDouble.

def double[A: CanDouble](a: A): A = implicitly[CanDouble[A]].double(a)

scala> double(1)
res4: Int = 2

scala> double(0.4)
res5: Double = 0.8

scala> double("a")
res6: String = aa

理想情况下,您将所有类型类实例(例如StringDoublenumericDouble)放置在伴随对象CanDouble中.

Ideally, you would put all of the type class instances like StringDouble and numericDouble within the companion object CanDouble.

我认为结构类型在这里根本不起作用,因为不允许您在结构细化之外定义的结构细化中使用抽象类型参数(类型参数T).从 SLS :

I don't think a structural type can work at all here, because you are not allowed to use an abstract type parameter within the structural refinement that is defined outside of the refinement (the type parameter T). From the SLS:

在结构细化中的方法声明中,任何值参数的类型只能引用细化内包含的类型参数或抽象类型.也就是说,它必须引用方法本身的类型参数,或者引用细化中的类型定义.此限制不适用于方法的结果类型.

Within a method declaration in a structural refinement, the type of any value parameter may only refer to type parameters or abstract types that are contained inside the refinement. That is, it must refer either to a type parameter of the method itself, or to a type definition within the refinement. This restriction does not apply to the method's result type.

无论如何,通常都应避免使用结构类型,因为它们非常慢.在这种情况下,应首选类型类.

Structural types should typically be avoided, anyway, as they are quite slow. Type classes should be preferred in this scenario.

这篇关于在Scala中,为什么不能实现这样的琐碎的泛型函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆