在Scala中,为什么可以';我不能实现这样一个普通的函数吗?
我想要一个名为“double”的通用函数,其行为类似于此,可以应用于具有在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
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行未找到typeclassNum
:
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)