Traits似乎至少表面上与Haskell中的typeclasses类似,但是我看到人们写道他们之间存在一些差异。我想知道这些差异究竟是什么。
答案 0 :(得分:51)
在基本层面上,差别并不大,但他们仍然存在。
Haskell将类型类中定义的函数或值描述为'方法'正如traits描述它们所包含的对象中的OOP方法一样。但是,Haskell以不同的方式处理这些问题,将它们视为单独的值而不是将它们固定到对象上,因为OOP会引导人们去做。这是最明显的表面水平差异。
直到最近,Rust无法做的一件事是高阶类型特征,例如臭名昭着的Functor
和Monad
类型类。
这意味着Rust特征只能描述通常所谓的“具体类型”,换句话说,没有通用参数。但是,Haskell可以生成高阶类型类,它使用类似于高阶函数如何使用其他函数的类型,使用一个来描述另一个函数。我们无法实现Rust中提到的类型类,因为它不支持在Haskell中被认为是基本的,甚至是必需的这个特性。值得庆幸的是,最近已经使用associated items实现了这一点。*但是,这不是惯用的Rust,通常被认为是' hack'。
正如评论中所说,GHC(Haskell的主要编译器)支持类型类的更多选项,包括multi-parameter(即涉及的许多类型)类型类,以及{{3},这也是值得一提的。 },一个允许类型级计算的可爱选项,并引导到functional dependencies。据我所知,Rust既没有funDeps也没有类型系列,虽然它可能在未来。†
总而言之,虽然表面上的特质和类型似乎相当,但在考虑众多品质时,它们在进行更深入比较时确实存在很多差异。
的
的*在旁注中,尽管有特征,Swift没有这种更高级别的机制。这个语言的未来更新,可能吗?
†关于Haskell的类型类(包括更高类型的类型)的一篇很好的文章可以找到type families,而Rust的特性也是一个同样好的文章。保持here,但不幸的是有点过时了。
答案 1 :(得分:12)
我认为当前的答案忽略了Rust特征和Haskell类型类之间最根本的区别。这些差异与特征与面向对象的语言结构相关的方式有关。有关此信息,请参阅Rust book。
特征声明会创建特征类型。这意味着您可以声明此类型的变量(或者更确切地说,类型的引用)。您还可以使用特征类型作为函数,结构字段和类型参数实例化的参数。
特征引用变量可以在运行时包含不同类型的对象,只要引用对象的运行时类型实现特征。
// The shape variable might contain a Square or a Circle,
// we don't know until runtime
let shape: &Shape = get_unknown_shape();
// Might contain different kinds of shapes at the same time
let shapes: Vec<&Shape> = get_shapes();
这不是类型类的工作方式。 类型类不创建类型,因此您无法使用类名声明变量。 类型类在类型参数上充当边界,但类型参数必须使用具体类型实例化,而不是类型类本身。
您不能拥有实现相同类型类的不同类型的不同内容的列表。 (相反,在Haskell中使用存在类型来表达类似的东西。) 注1
特征方法可以动态调度。这与上面的子弹中描述的内容密切相关。
动态分派意味着参考点的对象的运行时类型用于确定通过引用调用的方法。
let shape: &Shape = get_unknown_shape();
// This calls a method, which might be Square.area or
// Circle.area depending on the runtime type of shape
print!("Area: {}", shape.area());
同样,在Haskell中使用存在类型。
在我看来,特征与类型类基本上是相同的概念。此外,它们还具有面向对象接口的功能。
(Haskell的类型类更高级,因为Haskell具有更高级的类型和扩展,如多参数类型类。但我认为它们基本相同。)
注1 :Rust的最新版本具有并更新以区分特征名称的用法作为类型以及特征名称作为边界的用法。我是一个特征类型,名称以dyn
关键字为前缀。有关详细信息,请参阅此answer。
答案 2 :(得分:7)
Rust的“traits”类似于Haskell的类型类。
与Haskell的主要区别在于traits只干涉带点符号的表达式,即a.foo(b)形式。
Haskell类型类扩展到更高阶类型。 Rust特征只支持更高阶类型,因为它们在整个语言中都缺失,即它不是特征和类型类之间的哲学差异