铁锈的特征看起来至少表面上类似于哈斯克尔的类型分类,但是我看到人们写到它们之间有一些区别。我想知道这些区别到底是什么。
- 我对铁锈不太了解。但在其他语言中,类似技术的常见障碍是更高的类型(例如,特征是否可以覆盖参数化类型,而不是参数化类型?)以及返回类型多态性(例如,特征类型是否可以出现在函数的结果中,而不是参数中的任何地方?)在哈斯克尔,前者的一个例子是class Functor f where fmap :: (a -> b) -> (f a -> f b);后者的一个例子是class Bounded a where maxBound :: a。
- GHC还支持多参数类型类(即涉及多个类型的特性)和功能依赖性,尽管这不是官方haskell规范的一部分。从链接建议的rust语法来看,它只能支持一次超过一种类型的特性,尽管这种判断又不是基于经验的。
- @存在Danielwagner返回类型多态性(例如std::default),多参数特征有点作用(包括类似的功能依赖性),尽管Afaik One需要解决第一个参数的特权问题。但没有香港电话。他们在遥远的未来愿望清单上,但还不在地平线上。
- 另一个区别是对孤立实例的处理。Rust试图在一个特性的新请求可以被写入的地方有更严格的一致性规则。请参阅本讨论了解更多详细信息(尤其是这里)
- Rust现在支持相关的类型和平等约束,尽管它们不如Haskell的类型家族强大。它也有通过特征对象存在的类型。
- @丹尼尔瓦格纳:我认为参数化的特性和多参数类型类类似。Self只是另一个"输入参数",它得到了一些特殊的处理,并且是隐式的。
- 我对haskell类型类还不太了解,但是这里有traits.doc.rust-lang.org/reference.html traits的rust引用(它现在已经过时了)和接受的+部分实现的rfc,它们将相关项添加到traits github.com/rust lang/rfc s/blob/master/text/…
在基本层面上,没有太大的区别,但它们仍然存在。
haskell将typeclass中定义的函数或值描述为"methods",就像traits在它们所包含的对象中描述oop方法一样。但是,haskell处理这些问题的方式不同,将它们视为单个值,而不是像OOP那样将它们固定到对象上。这是最明显的表面水平差异。
直到最近,Rust做不到的一件事是高阶类型的特性,比如臭名昭著的Functor和Monad类型。
这意味着铁锈特性只能描述通常所说的"具体类型",换句话说,一个没有一般性论据的类型。但是,haskell可以生成使用类似于高阶函数如何使用其他函数的类型的高阶类型类,使用一个函数来描述另一个函数。我们不能在Rust中实现前面提到的类型类,因为它不支持在Haskell中被认为是基本的甚至是必要的这个特性。值得庆幸的是,这一点最近在相关项目中得到了实施。*然而,这并不是惯用的铁锈,通常被认为是"黑客"。
正如评论中所说,还可以提到,ghc(haskell的主要编译器)支持类型类的进一步选项,包括多参数(即涉及的许多类型)类型类和函数依赖性,这是一个允许类型级计算的可爱选项,并导致类型族。据我所知,Rust既没有眼底,也没有类型家族,尽管它可能在未来。
总的来说,虽然特征和类型类表面上看起来是等价的,但在考虑众多的品质时,如果比较深入,它们确实有很多不同。
*另一方面,斯威夫特,尽管有特点,却没有这样高的分型机制。可能是语言的未来更新?
?这里有一篇关于haskell的字体类(包括更高级的字体类)的好文章,还有一篇关于rust特性的同样好的文章被保存在这里,不过不幸的是有点过时了。
- 铁锈仍然没有任何更高级的种类。""声名狼藉"需要辩护。函数作为一个概念是非常普遍和有用的。类型族与关联的类型相同。功能依赖性与相关类型(包括Haskell)基本上是冗余的。生锈的东西没有WRT。眼底是注射剂注释。你倒过来看,Rust的特征和Haskell的类型在表面上是不同的,但是当你往下看的时候,很多不同就消失了。仍然存在的差异主要是语言所处的不同领域所固有的。
我认为目前的答案忽略了铁锈特性和哈斯克尔类型之间最根本的区别。这些差异与特征与面向对象的语言结构相关的方式有关。有关这方面的信息,请参阅铁锈书。
特性声明创建特性类型。这意味着您可以声明此类类型的变量(或者更确切地说,类型的引用)。您还可以使用特征类型作为函数、结构字段和类型参数实例化的参数。
只要被引用对象的运行时类型实现了特征,特征引用变量在运行时可以包含不同类型的对象。
1 2 3 4 5 6
| // 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
特征方法可以动态调度。这与上面的项目符号中描述的内容密切相关。
动态调度意味着使用引用点的对象的运行时类型来确定通过引用调用的方法。
1 2 3 4 5
| 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关键字。有关更多信息,请参见此答案的示例。
- "类型类不创建类型"——我认为最好把dyn Trait理解为存在类型的一种形式,因为它们与特性/类型类相关。我们可以把dyn看作是一个边界上的运算符,将它们投影到类型,即dyn : List Bound -> Type。把这个想法带到haskell,以及关于"所以不能用类名声明变量。",我们可以在haskell:data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t中间接地这样做。定义好后,我们可以与[D True, D"abc", D 42] :: [D Show]合作。
Rust的"特性"类似于Haskell的类型分类。
与haskell的主要区别在于,特征只会影响带点符号的表达式,即A.foo(b)形式的表达式。
Haskell类型类扩展到高阶类型。Rust特性不支持高阶类型,因为它们在整个语言中都是缺失的,也就是说,特性和类型类之间没有哲学上的区别。
- Rust中的特征并不仅仅是"用点表示法来干预表达式"。例如,考虑没有方法,只有非方法关联函数的Default特性。