Mientras aprendo Haskell, noté su clase de tipo , que se supone que es un gran invento que se originó en Haskell.
Sin embargo, en la página de Wikipedia sobre la clase de tipo :
El programador define una clase de tipos especificando un conjunto de nombres de funciones o constantes, junto con sus respectivos tipos, que deben existir para cada tipo que pertenece a la clase.
Lo que me parece bastante cercano a la interfaz de Java (citando la página de la interfaz de Wikipedia (Java) ):
Una interfaz en el lenguaje de programación Java es un tipo abstracto que se utiliza para especificar una interfaz (en el sentido genérico del término) que las clases deben implementar.
Estos dos se ven bastante similares: la clase de tipo limita el comportamiento de un tipo, mientras que la interfaz limita el comportamiento de una clase.
Me pregunto cuáles son las diferencias y similitudes entre la clase de tipos en Haskell y la interfaz en Java, o tal vez son fundamentalmente diferentes.
EDITAR: noté que incluso haskell.org admite que son similares . Si son tan similares (¿o lo son?), ¿Por qué la clase de tipos se trata con tanta exageración?
MÁS EDITAR: ¡ Vaya, tantas respuestas geniales! Supongo que tendré que dejar que la comunidad decida cuál es la mejor. Sin embargo, al leer las respuestas, todas parecen decir simplemente que "hay muchas cosas que la clase de tipos puede hacer mientras que la interfaz no puede o tiene que hacer frente a los genéricos" . No puedo evitar preguntarme, ¿hay algo que las interfaces puedan hacer mientras que las clases de tipos no? Además, noté que Wikipedia afirma que typeclass se inventó originalmente en el artículo de 1989 * "Cómo hacer que el polimorfismo ad-hoc sea menos ad hoc", mientras Haskell todavía está en su cuna, mientras que el proyecto Java se inició en 1991 y se lanzó por primera vez en 1995. Entonces, tal vez en lugar de que typeclass sea similar a las interfaces, es al revés, que las interfaces fueron influenciadas por typeclass.¿Hay algún documento / documentación que apoye o refute esto? Gracias por todas las respuestas, ¡todas son muy esclarecedoras!
¡Gracias por todas las entradas!
Respuestas:
Yo diría que una interfaz es como una clase de tipos
SomeInterface t
donde todos los valores tienen el tipot -> whatever
(dondewhatever
no contienet
). Esto se debe a que con el tipo de relación de herencia en Java y lenguajes similares, el método llamado depende del tipo de objeto en el que se invoca y nada más.Eso significa que es muy difícil hacer cosas como
add :: t -> t -> t
con una interfaz, donde es polimórfica en más de un parámetro, porque no hay forma de que la interfaz especifique que el tipo de argumento y el tipo de retorno del método es del mismo tipo que el tipo de el objeto al que se llama (es decir, el tipo "self"). Con Generics, hay formas de falsificar esto creando una interfaz con un parámetro genérico que se espera que sea del mismo tipo que el objeto en sí, como cómo loComparable<T>
hace, donde se espera que lo usesFoo implements Comparable<Foo>
para que elcompareTo(T otherobject)
tipo de tenga tipot -> t -> Ordering
. Pero eso aún requiere que el programador siga esta regla, y también causa dolores de cabeza cuando la gente quiere hacer una función que use esta interfaz, tienen que tener parámetros recursivos de tipo genérico.Además, no tendrá cosas como
empty :: t
porque no está llamando a una función aquí, por lo que no es un método.fuente
Lo que es similar entre interfaces y clases de tipos es que nombran y describen un conjunto de operaciones relacionadas. Las operaciones en sí mismas se describen mediante sus nombres, entradas y salidas. Asimismo, puede haber muchas implementaciones de estas operaciones que probablemente difieran en su implementación.
Con eso fuera del camino, aquí hay algunas diferencias notables:
En general, creo que es justo decir que las clases de tipos son más potentes y flexibles que las interfaces. ¿Cómo definiría una interfaz para convertir una cadena en algún valor o instancia del tipo de implementación? Ciertamente no es imposible, pero el resultado no sería intuitivo ni elegante. ¿Alguna vez ha deseado que fuera posible implementar una interfaz para un tipo en alguna biblioteca compilada? Ambos son fáciles de lograr con clases de tipos.
fuente
Las clases de tipos se crearon como una forma estructurada de expresar "polimorfismo ad-hoc", que es básicamente el término técnico para las funciones sobrecargadas . Una definición de clase de tipo se parece a esto:
Lo que esto significa es que, cuando usa aplicar la función
foo
a algunos argumentos de un tipo que pertenecen a la claseFoobar
, busca una implementación defoo
específica para ese tipo y la usa. Esto es muy similar a la situación con la sobrecarga de operadores en lenguajes como C ++ / C #, excepto que es más flexible y generalizado.Las interfaces tienen un propósito similar en los lenguajes OO, pero el concepto subyacente es algo diferente; Los lenguajes OO vienen con una noción incorporada de jerarquías de tipos que Haskell simplemente no tiene, lo que complica las cosas de alguna manera porque las interfaces pueden involucrar tanto la sobrecarga por subtipos (es decir, llamar a métodos en instancias apropiadas, subtipos que implementan interfaces que hacen sus supertipos) y por despacho basado en tipo plano (ya que dos clases que implementan una interfaz pueden no tener una superclase común que también la implemente). Dada la enorme complejidad adicional introducida por el subtipo, sugiero que es más útil pensar en las clases de tipos como una versión mejorada de funciones sobrecargadas en un lenguaje que no sea OO.
También vale la pena señalar que las clases de tipos tienen medios de distribución mucho más flexibles: las interfaces generalmente se aplican solo a la clase única que las implementa, mientras que las clases de tipos se definen para un tipo , que puede aparecer en cualquier lugar de la firma de las funciones de la clase. El equivalente de esto en interfaces OO sería permitir que la interfaz defina formas de pasar un objeto de esa clase a otras clases, definir métodos estáticos y constructores que seleccionarían una implementación basada en qué tipo de retorno se requiere en el contexto de llamada, definir métodos que tomar argumentos del mismo tipo que la clase que implementa la interfaz y varias otras cosas que realmente no se traducen en absoluto.
En resumen: tienen propósitos similares, pero la forma en que funcionan es algo diferente, y las clases de tipos son significativamente más expresivas y, en algunos casos, más simples de usar debido a que trabajan con tipos fijos en lugar de partes de una jerarquía de herencia.
fuente
He leído las respuestas anteriores. Siento que puedo responder un poco más claramente:
Una "clase de tipo" Haskell y una "interfaz" Java / C # o un "rasgo" Scala son básicamente análogos. No existe una distinción conceptual entre ellos, pero existen diferencias de implementación:
fuente
En Mentes maestras de programación , hay una entrevista sobre Haskell con Phil Wadler, el inventor de las clases de tipos, quien explica las similitudes entre las interfaces en Java y las clases de tipos en Haskell:
Entonces, las clases de tipos están relacionadas con interfaces, pero la correspondencia real sería un método estático parametrizado con un tipo como el anterior.
fuente
Vea la charla de Phillip Wadler Fe, evolución y lenguajes de programación . Wadler trabajó en Haskell y fue un importante colaborador de Java Generics.
fuente
Lea Extensión de software e integración con clases de tipos donde se dan ejemplos de cómo las clases de tipos pueden resolver una serie de problemas que las interfaces no pueden.
Los ejemplos enumerados en el documento son:
fuente
No puedo hablar con el nivel de "exageración", si así me parece bien. Pero sí, las clases de tipos son similares en muchos aspectos. Una diferencia en la que puedo pensar es que Haskell puede proporcionar comportamiento para algunas de las operaciones de la clase de tipo :
que muestra que hay dos operaciones, iguales
(==)
y no iguales(/=)
, para cosas que son instancias de laEq
clase de tipos. Pero la operación no igual se define en términos de iguales (de modo que solo tendría que proporcionar uno) y viceversa.Entonces en probablemente-no-legal-Java eso sería algo como:
y la forma en que funcionaría es que solo necesitaría proporcionar uno de esos métodos para implementar la interfaz. Entonces, diría que la capacidad de proporcionar una especie de implementación parcial del comportamiento que desea en el nivel de interfaz es una diferencia.
fuente
Son similares (léase: tienen un uso similar), y probablemente se implementan de manera similar: las funciones polimórficas en Haskell toman bajo el capó una 'vtable' que enumera las funciones asociadas con la clase de tipos.
Esta tabla a menudo se puede deducir en tiempo de compilación. Probablemente esto sea menos cierto en Java.
Pero esta es una tabla de funciones , no de métodos . Los métodos están vinculados a un objeto, las clases de tipos de Haskell no.
Véalos como los genéricos de Java.
fuente
Como dice Daniel, las implementaciones de interfaz se definen por separado de las declaraciones de datos. Y como han señalado otros, existe una forma sencilla de definir operaciones que utilizan el mismo tipo libre en más de un lugar. Entonces es fácil de definir
Num
como una clase de tipos. Por lo tanto, en Haskell obtenemos los beneficios sintácticos de la sobrecarga de operadores sin tener realmente operadores sobrecargados mágicamente, solo clases de tipos estándar.Otra diferencia es que puede usar métodos basados en un tipo, ¡incluso cuando aún no tiene un valor concreto de ese tipo!
Por ejemplo
read :: Read a => String -> a
,. Entonces, si tiene suficiente información de otro tipo sobre cómo usará el resultado de una "lectura", puede dejar que el compilador averigüe qué diccionario usar para usted.También puede hacer cosas como las
instance (Read a) => Read [a] where...
que le permiten definir una instancia de lectura para cualquier lista de cosas legibles. No creo que eso sea posible en Java.Y todo esto es solo clases de tipos estándar de un solo parámetro sin engaños. Una vez que introducimos las clases de tipos multiparamétricas, se abre un nuevo mundo de posibilidades, y más aún con las dependencias funcionales y las familias de tipos, que le permiten integrar mucha más información y cálculo en el sistema de tipos.
fuente