operadores - ¿Cuál es la diferencia entre los rasgos en Rust y las clases de tipos en Haskell?
ord haskell (3)
Traits en Rust parecen al menos superficialmente similares a las typeclasses en Haskell, sin embargo, he visto a personas escribir que hay algunas diferencias entre ellos. Me preguntaba exactamente cuáles son estas diferencias.
Creo que las respuestas actuales pasan por alto las diferencias más fundamentales entre los rasgos de óxido y las clases de tipo Haskell. Estas diferencias tienen que ver con la forma en que los rasgos están relacionados con las construcciones de lenguaje orientado a objetos. Para obtener información sobre esto, consulte el libro Rust .
-
Una declaración de rasgo crea un tipo de rasgo . Esto significa que puede declarar variables de ese tipo (o más bien, referencias del tipo). También puede usar tipos de rasgos como parámetros en funciones, campos de estructura e instancias de parámetros de tipo.
Una variable de referencia de rasgo puede en tiempo de ejecución contener objetos de diferentes tipos, siempre que el tipo de tiempo de ejecución del objeto referenciado implemente el rasgo.
// 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();
No es así como funcionan las clases de tipos. Las clases de tipos no crean tipos , por lo que no puede declarar variables con el nombre de la clase. Las clases de tipos actúan como límites en los parámetros de tipo, pero los parámetros de tipo deben instanciarse con un tipo concreto, no con la clase de tipo en sí.
No puede tener una lista de diferentes cosas de diferentes tipos que implementan la misma clase de tipo. (En cambio, los tipos existenciales se usan en Haskell para expresar algo similar). Nota 1
-
Los métodos de rasgos pueden ser enviados dinámicamente . Esto está fuertemente relacionado con las cosas que se describen en la sección anterior.
Despacho dinámico significa que el tipo de tiempo de ejecución del objeto al que apunta una referencia se usa para determinar qué método se llama a través de la referencia.
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());
Nuevamente, los tipos existenciales se usan para esto en Haskell.
En conclusión
Me parece que los rasgos son esencialmente el mismo concepto que las clases de tipos. Además, tienen la funcionalidad de interfaces orientadas a objetos.
Por otro lado, las clases de tipos de Haskell son más avanzadas, ya que Haskell tiene, por ejemplo, tipos y extensiones de tipo superior, como clases de tipos de parámetros múltiples. Pero creo que son esencialmente lo mismo.
Nota 1
: Las versiones recientes de Rust tienen una actualización para diferenciar el uso de nombres de rasgos como tipos y el uso de nombres de rasgos como límites.
En un tipo de rasgo, el nombre está precedido por la palabra clave
dyn
.
Consulte, por ejemplo, esta
answer
para obtener más información.
En el nivel básico, no hay mucha diferencia, pero todavía están allí.
Haskell describe funciones o valores definidos en una clase de tipos como ''métodos'', del mismo modo que los rasgos describen métodos OOP en los objetos que encierran. Sin embargo, Haskell se ocupa de estos de manera diferente, tratándolos como valores individuales en lugar de fijarlos a un objeto como OOP lo llevaría a hacerlo. Esta es la diferencia de nivel de superficie más obvia que existe.
Lo único que Rust no pudo hacer por un tiempo fueron
los rasgos mecanografiados de orden superior
, como las infames clases de tipos
Functor
y Monad.
Esto significa que los rasgos de óxido solo podrían describir lo que a menudo se llama un "tipo concreto", en otras palabras, uno sin un argumento genérico. Haskell desde el principio podría crear clases de tipos de orden superior que usan tipos similares a cómo las funciones de orden superior usan otras funciones: usar una para describir otra. Durante un período de tiempo esto no fue posible en Rust, pero desde que se implementaron los elementos asociados , estos rasgos se han vuelto comunes e idiomáticos.
Entonces, si ignoramos las extensiones, no son exactamente las mismas, pero cada una puede aproximarse a lo que la otra puede hacer.
También es mencionable, como se dice en los comentarios, que GHC (compilador principal de Haskell) admite más opciones para clases de tipos, incluidas clases de tipos de multi-parameter (es decir, muchos tipos involucrados) y dependencias funcionales , una opción encantadora que permite cálculos a nivel de tipo , y lleva a escribir familias . Que yo sepa, Rust no tiene funDeps ni familias de tipos, aunque puede que en el futuro. †
En general, los rasgos y las clases de tipos tienen diferencias fundamentales que, debido a la forma en que interactúan, los hacen actuar y parecer bastante similares al final.
† Puede encontrar un buen artículo sobre las clases de tipos de Haskell (incluidas las de tipo más alto) here , y el capítulo Rust by Example sobre rasgos puede encontrarse aquí
Los "rasgos" de Rust son análogos a las clases de tipos de Haskell.
La principal diferencia con Haskell es que los rasgos solo intervienen para expresiones con notación de puntos, es decir, de la forma a.foo (b).
Las clases de tipo Haskell se extienden a tipos de orden superior. Los rasgos de óxido solo no admiten tipos de orden superior porque faltan en todo el lenguaje, es decir, no es una diferencia filosófica entre rasgos y clases de tipos