Rust 中的特征和 Haskell 中的类型类有什么区别?

What is the difference between traits in Rust and typeclasses in Haskell?

Traits in Rust seem at least superficially similar to typeclasses in Haskell,但是我看到有人写道它们之间存在一些差异。我想知道这些差异到底是什么。

Rust 的“特征”类似于 Haskell 的类型 类。

与 Haskell 的主要区别是特征仅干预带有点符号的表达式,即形式 a.foo(b).

Haskell 类型 类 扩展到高阶类型。 Rust traits 仅不支持高阶类型,因为它们在整个语言中缺失,即这不是 traits 和类型之间的哲学差异 类

在基础层面上,差别不大,但还是有的。

Haskell 将类型类中定义的函数或值描述为 'methods',就像特征描述它们所包含的对象中的 OOP 方法一样。然而,Haskell 以不同的方式处理这些,将它们视为单独的值,而不是像 OOP 那样将它们固定到一个对象。这是最明显的表面水平差异。

Rust 暂时无法做的一件事是 高阶类型特征,例如臭名昭​​著的 FunctorMonad 类型类。

这意味着 Rust traits 只能描述通常称为 'concrete type' 的东西,换句话说,没有通用参数的东西。 Haskell 从一开始就可以创建高阶类型类,它使用类似于高阶函数使用其他函数的类型:使用一个来描述另一个。在一段时间内,这在 Rust 中是不可能的,但是自从 associated items 已经实现后,这些特性就变得司空见惯了。

因此,如果我们忽略扩展,它们并不完全相同,但每个都可以近似于另一个的功能。

正如评论中所说,还值得一提的是,GHC(Haskell 的主要编译器)支持类型类的更多选项,包括 multi-parameter (i.e. many types involved) typeclasses, and functional dependencies, a lovely option that allows for type-level computations, and leads on to type families。据我所知,Rust 既没有 funDeps 也没有类型族,尽管将来可能会有。†

总而言之,traits 和 typeclass 有根本的区别,由于它们相互作用的方式,使它们最终表现得非常相似。


† 可以找到一篇关于 Haskell 类型类(包括更高类型的)的好文章 here, and the Rust by Example chapter on traits may be found here

我认为当前的答案忽略了 Rust 特征和 Haskell 类型 classes 之间最根本的区别。这些差异与特征与面向对象语言结构相关的方式有关。有关这方面的信息,请参阅 Rust book.

  1. 特征声明创建特征类型。这意味着您可以声明这种类型的变量(或者更确切地说,该类型的引用)。您还可以将特征类型用作函数、结构字段和类型参数实例化的参数。

    特征引用变量在运行时可以包含不同类型的对象,只要引用对象的运行时类型实现了特征。

    // 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();
    

    这不是类型 class 的工作方式。 类型 classes 不创建类型 ,因此您不能使用 class 名称声明变量。 类型 classes 作为类型参数的边界,但类型参数必须用具体类型实例化,而不是类型 class 本身。

    您不能拥有实现相同类型的不同类型的不同事物的列表 class。 (相反,Haskell 中使用存在类型来表达类似的东西。)注 1

  2. 可以动态调度 Trait 方法。这与上一节中描述的内容密切相关。

    动态调度是指通过引用指向的对象的运行时类型来确定通过引用调用哪个方法。

    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 中使用了存在类型。

总结

在我看来,特征在很多方面与类型 classes 是相同的概念。此外,它们还具有面向对象接口的功能。

另一方面,Haskell的类型class更高级。 Haskell 具有例如更高种类的类型和扩展,如多参数类型 classes.


注 1:最新版本的 Rust 进行了更新,以区分特征名称作为类型的用法和特征名称作为边界的用法。在特征类型中,名称以 dyn 关键字为前缀。有关详细信息,请参阅此 示例。