layout haskell syntax syntactic-sugar

layout - Sintaxis de registro de Haskell



syntax syntactic-sugar (3)

La sintaxis del registro de Haskell es considerada por muchos como una verruga en un lenguaje por lo demás elegante, a causa de su fea sintaxis y contaminación del espacio de nombres. Por otro lado, a menudo es más útil que la alternativa basada en la posición.

En lugar de una declaración como esta:

data Foo = Foo { fooID :: Int, fooName :: String } deriving (Show)

Me parece que algo en esta línea sería más atractivo:

data Foo = Foo id :: Int name :: String deriving (Show)

Estoy seguro de que debe haber una buena razón por la que me estoy perdiendo, pero ¿por qué se adoptó la sintaxis de registro tipo C sobre un enfoque más limpio basado en el diseño?

En segundo lugar, ¿hay algo en preparación para resolver el problema del espacio de nombres, por lo que podemos escribir id foo lugar de fooID foo en futuras versiones de Haskell? (Aparte de las soluciones basadas en clase de tipo longwinded actualmente disponibles).


Bueno, si nadie más va a intentarlo, entonces tomaré otra puñalada (un poco más investigada) para responder estas preguntas.

tl; dr

Pregunta 1: Así es como rodaban los dados. Fue una elección circunstancial y se estancó.

Pregunta 2: Sí (sorta). Varias partes diferentes ciertamente han estado pensando sobre el tema.

Siga leyendo para obtener una explicación muy larga para cada respuesta, basada en enlaces y citas que considero relevantes e interesantes.

¿Por qué se adoptó la sintaxis de registro tipo C sobre un enfoque más limpio basado en el diseño?

Los investigadores de Microsoft escribieron un artículo de History of Haskell . La Sección 5.6 habla de registros. Voy a citar el primer pedacito, que es perspicaz:

Una de las omisiones más obvias de las primeras versiones de Haskell fue la ausencia de registros, ofreciendo campos con nombre. Dado que los registros son extremadamente útiles en la práctica, ¿por qué fueron omitidos?

Los Microsofties luego responden su propia pregunta

La razón más fuerte parece haber sido que no había un diseño "correcto" obvio.

Puede leer el documento usted mismo para conocer los detalles, pero dicen que Haskell finalmente adoptó la sintaxis del registro debido a la "presión de los campos con nombre en las estructuras de datos".

En el momento en que el diseño de Haskell 1.3 estaba en marcha, en 1993, la presión del usuario para los campos nombrados en las estructuras de datos era fuerte, por lo que el comité finalmente adoptó un diseño minimalista ...

Usted pregunta por qué es por qué es? Bueno, por lo que entiendo, si los primeros Haskellers se salieron con la suya, es posible que nunca hubiéramos tenido una sintaxis de registro en primer lugar. Al parecer, la idea la empujaron a Haskell personas que ya estaban acostumbradas a la sintaxis similar a C, y estaban más interesadas en conseguir cosas parecidas a C en Haskell en lugar de hacer las cosas "de la manera Haskell". (Sí, me doy cuenta de que esta es una interpretación extremadamente subjetiva. Podría estar absolutamente equivocado, pero a falta de mejores respuestas, esta es la mejor conclusión que puedo extraer).

¿Hay algo en la tubería para resolver el problema del espacio de nombres?

En primer lugar, no todos sienten que es un problema. Hace unas semanas, un entusiasta de la Racket me explicó (y a otros) que tener diferentes funciones con el mismo nombre era una mala idea, porque complica el análisis de "¿qué hace la función llamada ___ do?" No es, de hecho, una función, sino muchas. La idea puede ser un problema adicional para Haskell, ya que complica la inferencia de tipo.

En una ligera tangente, los Microsoft tienen cosas interesantes que decir acerca de las clases de Haskell:

Fue una feliz coincidencia de tiempo que Wadler y Blott produjeran esta idea clave justo en el momento en que el diseño del lenguaje aún estaba en flujo.

No olvides que Haskell era joven una vez. Algunas decisiones se tomaron simplemente porque fueron hechas.

De todos modos, hay algunas formas interesantes de abordar este "problema":

Escriba Directed Name Resolution , una modificación propuesta a Haskell (mencionada en los comentarios anteriores). Simplemente lea esa página para ver que toca muchas áreas del idioma. En general, no es una mala idea. Se ha pensado mucho para que no choque con cosas. Sin embargo, todavía requerirá mucha más atención para incorporarlo al ahora (más) maduro lenguaje Haskell.

Otro periódico de Microsoft, OO Haskell , propone específicamente una extensión del lenguaje Haskell para apoyar la "sobrecarga ad hoc". Es bastante complicado, por lo que solo tendrá que verificar la Sección 4 por su cuenta. La esencia de esto es automáticamente (?) Inferir tipos "Has" y agregar un paso adicional a la verificación de tipo que ellos llaman "mejora", vagamente delineado en las citas selectivas que siguen:

Dada la restricción de clase Has_m (Int -> C -> r) solo hay una instancia para m que coincida con esta restricción ... Dado que hay exactamente una opción, debemos hacerla ahora, y eso a su vez corrige que r sea Int . Por lo tanto obtenemos el tipo esperado para f : f :: C -> Int -> IO Int ... [esto] es simplemente una opción de diseño, y una basada en la idea de que la clase Has_m está cerrada

Disculpas por las citas incoherentes; si eso lo ayuda en absoluto, entonces genial, de lo contrario, simplemente lea el documento. Es una idea complicada (pero convincente).

Chris Done ha utilizado Template Haskell para proporcionar tipado de pato en Haskell de una manera vagamente similar al del papel OO Haskell (usando tipos "Has"). Algunas muestras de sesiones interactivas de su sitio:

λ> flap ^. donald *Flap flap flap* λ> flap ^. chris I''m flapping my arms! fly :: (Has Flap duck) => duck -> IO () fly duck = do go; go; go where go = flap ^. duck λ> fly donald *Flap flap flap* *Flap flap flap* *Flap flap flap*

Esto requiere un poco de repetición / sintaxis inusual, y yo personalmente preferiría atenerme a las clases de tipos. Pero felicitaciones a Chris Done por publicar libremente su trabajo práctico en el área.


Solo pensé en agregar un enlace al problema del espacio de nombres. Parece que los campos de registro sobrecargados para GHC vienen en GHC 7.10 (y probablemente ya estén en HEAD), usando la extensión OverloadedRecordFields .

Esto permitiría la sintaxis tal como

data Person = Person { id :: Int, name :: String } data Company { name :: String, employees :: [Person] } companyNames :: Company -> [String] companyNames c = name c : map name (employees c)


[edit] Esta respuesta es solo algunos pensamientos al azar al respecto. Recomiendo mi otra respuesta sobre esta, porque para esa respuesta me tomé mucho más tiempo para buscar y hacer referencia al trabajo de otras personas.

Sintaxis de registro

Tomando algunas puñaladas en la oscuridad: su sintaxis propuesta "basada en el diseño" se parece mucho a las declaraciones de data sintaxis de data ; eso podría causar confusión para el análisis (?)

--record data Foo = Foo {i :: Int, s :: String} deriving (Show) --non-record data Foo = Foo Int String deriving (Show) --new-record data Foo = Foo i :: Int, s :: String deriving (Show) --record data LotsaInts = LI {a,b,c,i,j,k :: Int} --new-record data LostaInts = LI a,b,c,i,j,k :: Int

En este último caso, ¿a qué se aplica exactamente :: Int ? ¿Toda la declaración de datos?

Las declaraciones con la sintaxis del registro (actualmente) son similares a la sintaxis de construcción y actualización. La sintaxis basada en el diseño no sería más clara para estos casos; ¿Cómo se analizan esos signos adicionales?

let f1 = Foo {s = "foo1", i = 1} let f2 = f1 {s = "foo2"} let f1 = Foo s = "foo1", i = "foo2" let f2 = f1 s = "foo2"

¿Cómo se sabe que f1 s es una actualización de registro, a diferencia de una aplicación de función?

Espaciado de nombres

¿Qué sucede si deseas entremezclar el uso de tu id definida por la clase con la id del Prelude? ¿Cómo se especifica cuál está usando? ¿Puedes pensar en una mejor manera que las importaciones calificadas y / o la palabra clave de hiding ?

import Prelude hiding (id) data Foo = Foo {a,b,c,i,j,k :: Int, s :: String} deriving (Show) id = i

ghci> :l data.hs ghci> let foo = Foo 1 2 3 4 5 6 "foo" ghci> id foo 4 ghci> Prelude.id f1 Foo {a = 1, b = 2, c = 3, i = 4, j = 5, k = 6, s = "foo"}

Estas no son excelentes respuestas, pero son lo mejor que tengo. Personalmente, no creo que la sintaxis del registro sea tan fea. Siento que hay margen de mejora con el paso del espacio de nombres / módulos, pero no tengo idea de cómo hacerlo mejor.