在Scala中,为什么可以';我不能实现这样一个普通的函数吗?

在Scala中,为什么可以';我不能实现这样一个普通的函数吗?,scala,generics,functional-programming,type-inference,duck-typing,Scala,Generics,Functional Programming,Type Inference,Duck Typing,我想要一个名为“double”的通用函数,其行为类似于此,可以应用于具有def+(x:T):Tmethod: double("A") > "AA" double(1) > 2 double(0.2) > 0.4 所以我这样写这个函数: def double[T](x:T):T = { x+x } double x = x + x 但是,当我在REPL中运行它时,scala对此表示不满: scala> def double[T](x:T):T = { x+x } &l

我想要一个名为“double”的通用函数,其行为类似于此,可以应用于具有
def+(x:T):T
method:

double("A")
> "AA"
double(1)
> 2
double(0.2)
> 0.4
所以我这样写这个函数:

def double[T](x:T):T = { x+x }
double x = x + x
但是,当我在REPL中运行它时,scala对此表示不满:

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 }
                                  ^

只是想知道为什么我不能在Scala中这样做…

不是每种类型的
t
都有
+
方法,所以无法工作。奇怪的错误消息来自编译器,它将第一个
x
视为
字符串
,因为每种类型都有一个
toString
方法,这是它能将泛型
T
视为具有
+
的唯一方法。但是,
T
将被传递到
+
,而不是
String
,它需要第二次隐式转换才能让它工作——即使我们这样做了,它也会返回
String
,而不是
T

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

现在我们可以定义
double
方法,该方法需要类型类
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
理想情况下,您可以将所有类型类实例,如
StringDouble
numericDouble
放在伴随对象
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

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

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


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

不是每种类型
T
都有
+
方法,因此无法工作。奇怪的错误消息来自编译器,它将第一个
x
视为
字符串
,因为每种类型都有一个
toString
方法,这是它能将泛型
T
视为具有
+
的唯一方法。但是,
T
将被传递到
+
,而不是
String
,它需要第二次隐式转换才能让它工作——即使我们这样做了,它也会返回
String
,而不是
T

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

现在我们可以定义
double
方法,该方法需要类型类
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
理想情况下,您可以将所有类型类实例,如
StringDouble
numericDouble
放在伴随对象
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

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

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


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

这是何时使用类型类的一个完美示例

+
只是一个函数。您没有向编译器提供以下信息:

def +(t : T, t : T) : T = ...
你不能,因为你根本不知道t是什么

在这里,它的工作原理如下。您有一个名为Double的类型构造函数:

trait Doubles[T]{
  def double(t : T) : T
}
现在在一个伴生对象中,为了方便起见,我将重写您的double函数,如下所示:

object Doubles{
  def double[T](t : T)(implicit doubles : Doubles[T]) =    doubles.double(t)
}
也就是说,我可以加倍T,只要范围中有T的加倍,或者你显式地为我提供T的加倍。否则,我不能加倍T,你会得到一个编译器错误

下面是该类型类double[T]的实例:

object Implicits{
    //Now, you wouldn't want to have to write this for 
    //every kind of number.  Scala already provides you with a numeric
    //typeclass.  So here's a function that gives a Doubles[N] 
    //whenever you ask for a Doubles[Numeric[T]], i.e. a Doubles for a     
    //member of the Numeric typeclass:

    implicit def numDoubler[N](implicit num : Numeric[N]) : Doubles[N] = new Doubles[N]{
        def double(n : N) : N = num.plus(n,n)
    }

    implicit object stringDoubler extends Doubles[String]{
        def double(t : String) : String = t + t
    }

 //So something like this is no longer needed:
 // implicit object intDoubler extends Doubles[Int]{
 //   def double(t : Int) : Int = t + t
 // }

}

这是一个关于何时使用TypeClass的完美示例

+
只是一个函数。您没有向编译器提供以下信息:

def +(t : T, t : T) : T = ...
你不能,因为你根本不知道t是什么

在这里,它的工作原理如下。您有一个名为Double的类型构造函数:

trait Doubles[T]{
  def double(t : T) : T
}
现在在一个伴生对象中,为了方便起见,我将重写您的double函数,如下所示:

object Doubles{
  def double[T](t : T)(implicit doubles : Doubles[T]) =    doubles.double(t)
}
也就是说,我可以加倍T,只要范围中有T的加倍,或者你显式地为我提供T的加倍。否则,我不能加倍T,你会得到一个编译器错误

下面是该类型类double[T]的实例:

object Implicits{
    //Now, you wouldn't want to have to write this for 
    //every kind of number.  Scala already provides you with a numeric
    //typeclass.  So here's a function that gives a Doubles[N] 
    //whenever you ask for a Doubles[Numeric[T]], i.e. a Doubles for a     
    //member of the Numeric typeclass:

    implicit def numDoubler[N](implicit num : Numeric[N]) : Doubles[N] = new Doubles[N]{
        def double(n : N) : N = num.plus(n,n)
    }

    implicit object stringDoubler extends Doubles[String]{
        def double(t : String) : String = t + t
    }

 //So something like this is no longer needed:
 // implicit object intDoubler extends Doubles[Int]{
 //   def double(t : Int) : Int = t + t
 // }

}
在haskell中,您可以:

Prelude> let double x = x + x // (1)
Prelude> let quadruple x = double (double x) //(2)

Prelude> :t double
double :: Num a => a -> a
Prelude> :t quadruple
quadruple :: Num a => a -> a
在scala中,必须明确指定
Num

scala> def double[T: Numeric] (a: T) = implicitly[Numeric[T]].plus(a, a)
double: [T](a: T)(implicit evidence$1: Numeric[T])T

scala> def quadruple[T: Numeric](a: T) = double(double(a))
quadruple: [T](a: T)(implicit evidence$1: Numeric[T])T
因为haskell的类型推断更聪明。(1) st行未找到typeclass
Num

Prelude> :info Num
class Num a where
  (+) :: a -> a -> a //looks like structural types, but ...
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
    -- Defined in ‘GHC.Num’ //... but here is implementations found accross build - they are explicitly saying that they are instances of Num

instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’
Scala在结构类型方面也有问题——您不能定义多态结构类型(不仅如此——您不能定义多态lam)