Los rasgos en Rust parecen al menos superficialmente similares a las clases tipográficas en Haskell, sin embargo, he visto a personas escribir que hay algunas diferencias entre ellos. Me preguntaba exactamente cuáles son estas diferencias.
157
class Functor f where fmap :: (a -> b) -> (f a -> f b)
; Un ejemplo de esto último esclass Bounded a where maxBound :: a
.std::default
. Ej. ), Y los rasgos multiparamétricos tipo de trabajo (incluido un análogo de dependencias funcionales), aunque AFAIK uno necesita evitar el primer parámetro que se privilegia. Sin embargo, no hay HKT. Están en la lista de deseos del futuro lejano pero aún no en el horizonte.Respuestas:
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 clases infames
Functor
yMonad
tipográficas.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 dijo en los comentarios, que GHC (compilador principal de Haskell) admite más opciones para clases de tipos, incluidas clases de tipos de parámetros múltiples (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 superior) aquí , y el capítulo Rust by Example sobre rasgos puede encontrarse aquí
fuente
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.
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.
El 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.
Nuevamente, los tipos existenciales se usan para esto en Haskell.
En conclusión
Me parece que los rasgos son en muchos aspectos 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. Haskell tiene, por ejemplo, tipos y extensiones de tipo superior, como clases de tipos de parámetros múltiples.
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á prefijado por la
dyn
palabra clave. Consulte, por ejemplo, esta respuesta para obtener más información.fuente
dyn Trait
como una forma de escritura existencial en relación con los rasgos / clases de tipos. Podemos considerardyn
un operador en los límites que los proyecta a tipos, es decirdyn : List Bound -> Type
. Tomando esta idea a Haskell, y en lo que respecta a "por lo que no se puede declarar variables con el nombre de clase", podemos hacerlo indirectamente en Haskell:data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t
. Habiendo definido esto, podemos trabajar con[D True, D "abc", D 42] :: [D Show]
.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
fuente
Default
rasgo que no tiene métodos, solo funciones asociadas sin métodos.