simbolo - ¿Qué significa generalmente cuando un descriptor de acceso de Haskell lleva un guión bajo?
guion bajo teclado (4)
Un ejemplo de la biblioteca HaskellNet:
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, al menos de acuerdo con la convención de Haskell?
El código de plantilla de Haskell a veces busca identificadores que comienzan con un guión bajo.
Por ejemplo, los guiones bajos se utilizan para automatizar la generación de lentes.
Es meramente una buena práctica de programación.
Dado que las etiquetas de los campos de registro en Haskell son en realidad funciones con nombre de nivel superior, contaminan el espacio de nombres del módulo. Agregar un guión bajo a la etiqueta del campo significa que puede definir otra función con el mismo nombre.
Por analogía con un guión bajo que representa un patrón irrelevante, por ejemplo, fst (x, _) = x
, se usa un prefijo de guión bajo (en campos de registro o de otra manera) para indicar que cualquier persona que lea el código debe ignorar un identificador, o tal vez ignorado por el compilador para ciertos tipos de interacción del usuario, aunque se le haya dado un nombre por alguna razón.
Tenga en cuenta que esto no es simplemente una convención, sino que se basa en algo expresado explícitamente en el Informe Haskell :
El guión bajo, "_", se trata como una letra minúscula, y puede aparecer siempre que una letra minúscula puede. Sin embargo, "_" todo por sí mismo es un identificador reservado, utilizado como comodín en los patrones. Se recomienda a los compiladores que ofrecen advertencias para identificadores no utilizados que supriman dichas advertencias para identificadores que comienzan con un guión bajo. Esto permite a los programadores usar "_foo" para un parámetro que esperan que no se use.
Un ejemplo serían las definiciones destinadas a ser utilizadas por Template Haskell, que luego define identificadores equivalentes sin el guión bajo, como en el ejemplo común de generación de lentes basadas en campos de registro (que supongo que es lo que está haciendo su ejemplo). En este caso, los identificadores son más entradas para TH que una definición real; el código producido por TH puede o no puede usar los identificadores con el guión bajo subrayado.
Sin embargo, aparte de lo anterior, un prefijo de subrayado no hace nada diferente a un identificador de minúsculas regular.
Recientemente descubrí algo relevante a tu pregunta.
Quería controlar dónde se construyen las instancias de mi tipo, para demostrar 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, razoné, desde fuera de este módulo, ningún código podría crear un Coords no válido.
¡Estaba equivocado! y
son públicos, por lo que solo hay 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 registro del tipo. La forma de solo permitir 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 funciones regulares. La sintaxis de c { x = -1 }
no se compilará, y otros módulos no tienen acceso al selector de registro _x.
El problema se resolvió con guiones bajos ;-)