¿Se borran los tipos en Haskell?

13

Haskell tiene una noción de "funciones genéricas" que tiene cierta similitud aparente con el lisp común: al no tener experiencia con Haskell ni con el lisp común, podría ser muy aproximado aquí. Esto significa que se puede definir una to_stringinstalación genérica para definir una representación de cadena para todos los tipos. Por supuesto, la facilidad tiene que definirse en los casos especiales, pero hay una to_stringfunción cuya firma es α → string.

¿Se borran los tipos en Haskell, como en OCaml? En caso afirmativo, ¿cómo difiere la implementación de "funciones genéricas" en Haskell de la de lisp común, donde los tipos son dinámicos y, por lo tanto, no se borran?

Entiendo que los detalles de implementación son específicos del compilador, pero probablemente hay disposiciones comunes a muchas o todas las implementaciones.

usuario40989
fuente
55
Recuerde que probablemente no pueda definir una función de tipo no trivial a -> String. Lo más probable es que tenga una restricción de tipo, como Show a => a -> String.
DJG

Respuestas:

16

La respuesta hasta ahora es engañosa.

Es necesario hacer una distinción entre polimorfismo "paramétrico" y "sobrecarga ad-hoc". Paramétrico significa "se comporta de manera uniforme para todos los tipos a", mientras que "ad-hoc", a lo que Simon se refiere como polimórfico, cambia la implementación en función del tipo.

Ejemplos de ambos son reverse :: [a] -> [a], que es paramétrico y show :: Show a => a -> Stringque está "ad-hoc" sobrecargado.

Si desea una intuición más abstracta, creo que es útil considerar las clases de verbos del lenguaje natural que "funcionan" para todos los objetos, como "poseer" o "pensar" que no imponen restricciones al objeto, pero " abrir "requiere que de lo que estamos hablando se pueda abrir. Puedo "pensar en una puerta" y "abrir una puerta", mientras que no tiene sentido, por ejemplo, "abrir un árbol". Tomar el ejemplo aún más "abrir" es "polimórfico ad-hoc" como "abrir una ventana" y "abrir un ticket de queja con el servicio al cliente" son dos cosas muy diferentes. Si esto parece forzado, ¡olvídalo! Esto funciona para mi.

Sin embargo, ambos se resuelven en tiempo de compilación y de hecho se "borran". Modulo de varias extensiones de GHC y Template Haskell, etc. De hecho , los tipos se borran en tiempo de compilación y nunca se inspeccionan en tiempo de ejecución.

Las funciones polimórficas paramétricas se comportan de manera idéntica para todos los tipos, por lo que solo se debe generar una pieza de código, mientras que el compilador decide en el momento de la compilación qué versión de una función "clasificada por tipo" debe ejecutarse en un punto de programa particular. Esta es también la razón por la cual existe la restricción de una instancia por tipo por clase de tipo y la solución alternativa "newtype" correspondiente.

La implementación se detalla en el libro de texto de SPJ y el documento de Wadler y Blotts sobre clases de tipos .

Kris
fuente
¿Crees que debería borrar mi respuesta?
Simon Bergot
No, está bien con tu advertencia de no tocar el vocabulario exacto:)
Kris
¿Podría ampliar un poco sobre por qué GHC puede borrar tipos? ¿Es gracias a la escritura principal?
Simon Bergot
2
En general, en tiempo de ejecución, las funciones polimórficas paramétricas pueden existir solo una vez y llamar a las funciones polimórficas ad-hoc a través de algún método de despacho virtual o todo el código puede generarse para cada tipo por separado y las llamadas están vinculadas estáticamente. Cualquiera de las implementaciones es correcta desde el punto de vista del lenguaje (tenga en cuenta que Haskell no tiene tipos polimórficos; tal cosa solo se puede crear con la forallextensión GHC ).
Jan Hudec
¿Hay alguna extensión de GHC que no permita que se borren los tipos? La sobrecarga es estática y sin tipos totalmente dependientes no puedo pensar en nada
Daniel Gratzer
1

advertencia: mi experiencia en CS no es muy sólida, por lo que no siempre uso el vocabulario correcto, y podría estar equivocado en algunos puntos. De todos modos, aquí está mi entendimiento:

Creo que estás confundiendo genéricos con polimorfismo.

Los tipos genéricos como los que List<Foo>se usan para describir los tipos que toman otros tipos como parámetro y permiten una verificación de tipos más rica.

Una función genérica en Haskell podría ser count:: List a -> Int. Acepta listas de cualquier tipo y devuelve el número de elementos. Solo hay una implementación.

Por lo general, al definir Lista, no puede asumir nada sobre T. Solo puede almacenarlo y devolverlo.

Su to_stringfunción es una función polimórfica, lo que significa que se comportará de manera diferente en diferentes circunstancias. En haskell, esto se hace con typeclasses, que funciona como adjetivos para tipos.

Tiene una Showclase de tipo, que define una show :: a -> Stringfunción.

Cuando un tipo Fooimplementa la Showclase de tipo, debe proporcionar la definición de show. Este tipo se puede usar en funciones que requieren el Showcalificador (como en Show a => a -> whatever). Haskell no puede borrar tipos, ya que esas funciones tienen que encontrar la implementación correcta de la showfunción.

Simon Bergot
fuente
En el lenguaje común, las funciones polimórficas se denominan "funciones genéricas" IIRC y utilicé el mismo vocabulario (confuso). Su respuesta es útil y su argumento final bastante convincente. .-)
user40989
“Solo hay una implementación”, solo una que tenga sentido, claro, pero hay infinitas posibilidades. Una función de tipo a → b tiene | b | ^ | a | implementaciones posibles (considere el número de funciones de tipo Bool → Bool), y las listas no tienen límite de tamaño superior.
Jon Purdy