2011-03-20 17 views
41

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.Sintaxis del registro de Haskell

En lugar de una declaración como esta:

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

Me parece que algo en este sentido sería más atractiva:

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

Estoy seguro de que debe haber una buena razón por la que Me falta, 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 la tubería para resolver el problema del espacio de nombres, por lo que podemos escribir id foo en lugar de fooID foo en versiones futuras de Haskell? (Aparte de las soluciones basadas en clase de tipo longwinded actualmente disponibles.)

+14

La sintaxis está muy bien - lo que "verruga" no reside más en el hecho de que los registros no son extensibles (la gente quiere más potencia) y el problema de espacio de nombres .Como ya me he acostumbrado a la sintaxis habitual, es un caso en el que preferiría la sintaxis del diseño: tal vez nadie consideró la sintaxis del diseño cuando se diseñó Haskell. "Resolución de nombre basado en tipo" Se ha propuesto TBNR para el segundo número: mucha gente parecía encontrarlo semánticamente problemático (no ayuda el hecho de que la propuesta sea difícilmente escrita) pero a un miembro de GHC-HQ pareció gustarle , entonces tal vez aparecerá. –

+2

@Stephen Tetley: ¿Por qué no publica eso como respuesta? ¡Es genial! – fuz

+2

En mi comentario anterior me refiero a TDNR - 'Resolución de nombre de tipo dirigido' desafortunadamente en este caso Stack Overflow no me permite editar el comentario. @FUZxxl - gracias, pero lo consideraría una opinión, por lo tanto, parece ser un comentario en lugar de una respuesta, y ciertamente no es una opinión universal. –

Respuesta

42

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

tl; dr

Pregunta 1: Eso es sólo la forma en que los dados tirados. 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 a través de un enfoque más limpio basado en el diseño?

Los investigadores de Microsoft escribieron un documento History of Haskell. La Sección 5.6 habla de registros. Voy a citar el primer bit pequeña, que es perspicaz:

Una de las omisiones más evidentes a partir de las primeras versiones de Haskell fue la ausencia de registros, ofreciendo campos nombrados. Dado que los registros son extremadamente útiles en la práctica, ¿por qué se omitieron?

Los Microsofties Luego responde a su propia pregunta

La razón más fuerte parece haber sido que no había obvia diseño “derecho”.

Puede leer el documento usted mismo para más 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 el diseño Haskell 1.3 estaba en marcha, en 1993, la presión del usuario de 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 obtener cosas parecidas a C en Haskell en lugar de hacer las cosas "de la manera Haskell". (Sí, me di cuenta que es una interpretación muy subjetiva. Podría estar equivocado, pero en ausencia de mejores respuestas, esta es la mejor conclusión que puedo sacar.)

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

Antes que nada, no todos sienten que es un problema. Hace unas semanas, un entusiasta de 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 un ligero tangente, los Microsofties tienen cosas interesantes que decir sobre las clases de tipos de Haskell:

Fue una feliz coincidencia de la oportunidad que Wadler y Blott pasaron a producir esta idea clave justo en el momento cuando el diseño del lenguaje todavía estaba en flujo.

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

De todas formas, hay algunas formas interesantes que este "problema" podría abordarse:

Type Directed Name Resolution, una propuesta de modificación de Haskell (mencionado en 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 documento de Microsoft, OO Haskell, propone específicamente una extensión del lenguaje Haskell para admitir "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 llaman "mejora", vagamente delineado en las siguientes citas selectivas:

Given the class restricción Has_m (Int -> C -> r) existe única instancia para m que coincide con esta restricción ... Como hay exactamente una opción, debemos hacerlo ahora, y que a su vez arregla r para que sea Int. Por lo tanto obtenemos el tipo esperado para f: f :: C -> Int -> IO Int ... [esto] es simplemente una elección diseño, y otro basado en la idea de que la clase Has_m se cierra

Disculpas por lo incoherente citar; 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 duck typing in Haskell de una manera vagamente similar al del papel OO Haskell (usando tipos "Has"). Unas muestras sesión interactiva 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 una sintaxis poco repetitivo/inusual, y yo personalmente preferiría que se adhieren a las clases de tipos. Pero felicitaciones a Chris Done por publicar libremente su trabajo práctico en el área.

+4

+1 para la historia del enlace de Haskell solo :) –

+23

"Los investigadores de Microsoft escribieron una historia del papel de Haskell" suena sesgado. El documento está escrito por cuatro de los diseñadores y creadores reales de Haskell; y sucede que hoy uno de ellos, Simon Peyton Jones, trabaja en Microsoft Research (y aloja sus trabajos en el sitio de MSR). Así que usted se refirió a las personas detrás del diseño de Haskell como "Microsofties", que creo que es una palabra ligeramente despectiva. –

+0

@Joker_vD ¿Sabes que puedes sugerir una edición, verdad? Si eso es cierto, me gustaría verlo editado. – corazza

5

[edit] Esta respuesta es solo algunos pensamientos al azar sobre el asunto. 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 unos puñaladas en la oscuridad: la sintaxis propuesta "basada en el diseño" se parece mucho a que no se graba-sintaxis data declaraciones; que pueda 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, ¿qué es exactamente :: Int aplicado a? ¿Toda la declaración de datos?

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

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 sabes f1 s es una actualización de registro, en lugar de una aplicación de función?

namespacing

¿Qué pasa si usted quiere mezclarse uso de su id con el Preludio de id definido de clase? ¿Cómo se especifica cuál está usando? ¿Puede pensar en una mejor manera que las importaciones calificadas y/o la palabra clave 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 grandes respuestas, pero son lo mejor que tengo. Personalmente, no creo que la sintaxis del registro sea que feo. Siento que hay margen de mejora con el paso del espacio de nombres/módulos, pero no tengo idea de cómo hacerlo mejor.

+0

Gracias por la respuesta, pero FWIW mi versión de diseño fue pensada como un ejemplo en lugar de una propuesta de esto es lo que debería ser. Estoy de acuerdo en que el tema del espacio de nombres es más importante, es de esperar que algo como el TDNR de @ stephen gane algo de tracción. –

+0

tal vez calificación automática/necesaria, como 'datos Foo = Foo {id :: Int}' y 'Foo.id'? mantiene la declaración conciso y el uso claro. –

8

Solo pensé en agregar un enlace al problema del espacio de nombres. Parece que overloaded record fields for GHC vienen en GHC 7.10 (y probablemente ya estén en HEAD), usando el OverloadedRecordFields extension.

Esto permitiría una sintaxis 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) 
+0

Solo una adición: OverloadedRecordFields evita los problemas que la respuesta de Dan Burton señala anteriormente con respecto a la inferencia de tipo al permitir que solo se utilicen en situaciones en las que el tipo ya se puede inferir claramente. En la práctica, esta es la mayoría de las situaciones (al menos si sigues el idioma común de declarar el tipo de funciones de nivel superior). Ahora se están trabajando en extensiones que amplían la variedad de formas en que se pueden usar en la inferencia de tipos o de forma polimórfica, incluida una clase propuesta de tipo de instancia automática que implementan todos los registros con un nombre de campo dado. – Jules

+0

... esto es algo similar al trabajo de plantilla Haskell de pato escrito por Chris Done citado en la respuesta de Dan Burton, excepto que no es necesario usar Template Haskell y con una sintaxis más directa. – Jules