2012-10-05 35 views
23

Un ejemplo de la biblioteca HaskellNet:¿Qué significa generalmente cuando un accesoador de registro Haskell conduce con un guión bajo?

data MailboxInfo = MboxInfo { _mailbox :: MailboxName 
          , _exists :: Integer 
          , _recent :: Integer 
          , _flags :: [Flag] 
          , _permanentFlags :: [Flag] 
          , _isWritable :: Bool 
          , _isFlagWritable :: Bool 
          , _uidNext :: UID 
          , _uidValidity :: UID 
          } 
       deriving (Show, Eq) 

¿El subrayado en los nombres de campo significa algo, si no es para el compilador continuación, al menos según la convención Haskell?

Respuesta

21

Por analogía a un guión que representa un patrón irrelevante, por ejemplo, fst (x, _) = x, un prefijo de subrayado (en campos de registro u otros) se utiliza para indicar que un identificador debe ser ignorado por cualquiera que lea el código, o tal vez ignorado por el compilador para ciertos tipos de interacción del usuario, aunque se le dio un nombre para algunos razón.

Tenga en cuenta que esto no es más que una convención, pero en base a algo que explícitamente se in the Haskell Report:

subrayado, "_", se trata como una letra minúscula, y puede ocurrir siempre que una minúscula la carta puede Sin embargo, "_" es un identificador reservado, utilizado como comodín en patrones. Se recomienda a los compiladores que ofrecen advertencias para los identificadores no utilizados que supriman dichas advertencias para identificadores que comiencen con guión bajo. Esto permite a los programadores usar "_foo" para un parámetro que esperan que no se use.

Un ejemplo sería las definiciones que están destinados para el uso de plantillas de Haskell, que a su vez define identificadores equivalentes sin el guión, como en el ejemplo común de generación de lentes basado en campos de registro (que supongo que es lo que su ejemplo está haciendo). En este caso, los identificadores son más entradas a TH que una definición real; el código producido por TH puede o no usar realmente los identificadores con el guión bajo subrayado.

Aparte de lo anterior, sin embargo, un prefijo de subrayado no hace nada diferente de un identificador de minúscula regular.

+3

Se puede ver cómo trata a GHC-subrayan el prefijo identificadores y advertencias aquí: http://www.haskell.org/ghc/docs/7.4.1/html/ users_guide/options-sanity.html – jberryman

+0

Consulte también esa propuesta para advertir acerca de los identificadores "no utilizados" que se usan de todos modos: https://ghc.haskell.org/trac/ghc/ticket/4959 – Lemming

1

Es simplemente una buena práctica de programación.

Dado que las etiquetas de campos de registro en Haskell son en realidad las funciones con nombre de nivel superior, contaminan el espacio de nombres del módulo. Agregar un guion bajo a la etiqueta del campo significa que usted es libre de definir otra función con el mismo nombre.

+2

Hay ocasiones en las que realmente desea use esa misma función, en cuyo caso un guión bajo sería inusual. Este es a menudo el caso con los tipos nuevos, p. 'newtype Reader e a = Reader {runReader :: e -> a}' –

1

Recientemente descubrí algo relevante para su pregunta.

Quería controlar dónde están construidas las instancias de mi tipo, para probar que cada valor es válido. Así lo declaré en su propio módulo:

module Coords (Coords(), -- hide the constructor 
       x,y,buildCoords) where 

data Coords = Coords { x :: Int, y :: Int } 

buildCoords :: Int -> Int -> Coords 
buildCoords x y | x < 0 || y < 0 = error "omg" 
buildCoords x y = Coords { x = x, y = y } 

Entonces, pensé, desde fuera de este módulo, no hay código podría crear un Coordenadas no válido.

¡Estaba equivocado! x y y son públicos, por lo que solo tiene que usar let c = buildCoords 1 1 in c { x = -1 } para obtener un valor de Coords no válido.

Pero esta sintaxis solo es posible porque xey son los selectores de registros del tipo.La forma de permitir solo valores válidos es la siguiente:

module Coords (Coords(), -- hide the constructor 
       x,y,buildCoords) where 

data Coords = Coords { _x :: Int, _y :: Int } 
x = _x 
y = _y 

buildCoords :: Int -> Int -> Coords 
buildCoords x y | x < 0 || y < 0 = error "omg" 
buildCoords x y = Coords { _x = x, _y = y } 

Ahora xey son solo funciones regulares. La sintaxis c { x = -1 } no se compilará y otros módulos no tendrán acceso al selector de registro _x.

El problema fue resuelto por guiones ;-)

+0

Pero deberías haber usado 'x_' y' y_' o similar para no pretender que los selectores de registros no se usen. – Lemming

Cuestiones relacionadas