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 ;-)
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
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