Что обычно означает, что когда аксессуар записи Haskell ведет с подчеркиванием?
Пример из библиотеки 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)
Подчеркивает ли подчеркивание в названиях полей что-то, если не компилятору, то, по крайней мере, согласно соглашению Haskell?
Ответы
Ответ 1
По аналогии с подчеркиванием, представляющим нерелевантный шаблон, например. fst (x, _) = x
, префикс подчеркивания (в полях записи или иначе) используется для указания того, что кто-либо читает код или игнорируется компилятором для определенных видов взаимодействия с пользователем, даже если ему было дано имя по какой-то причине.
Обратите внимание, что это не просто соглашение, но на основе явно выраженного в отчете Haskell:
Подчеркивание, "_", рассматривается как строчная буква и может происходить везде, где может быть строчная буква. Тем не менее, "_" само по себе является зарезервированным идентификатором, который используется в качестве "диких" карт в шаблонах. Компиляторам, которые предлагают предупреждения для неиспользуемых идентификаторов, рекомендуется подавлять такие предупреждения для идентификаторов, начинающихся с подчеркивания. Это позволяет программистам использовать "_foo" для параметра, который, как они ожидают, не используется.
Одним из примеров может быть определение, которое предназначено для использования Template Haskell, которое затем определяет эквивалентные идентификаторы без подчеркивания, как в распространенном примере генерации объективов на основе полей записи (что, я думаю, является тем, что делает ваш пример). В этом случае идентификаторы более вводятся в TH, чем фактическое определение; код, созданный TH, может или не может фактически использовать идентификаторы подчеркивания-префикса.
Кроме вышеописанного, префикс подчеркивания не делает ничего иначе, как обычный строчный идентификатор.
Ответ 2
Код шаблона Haskell иногда ищет идентификаторы, начинающиеся с подчеркивания.
Например, подчеркивания используются для автоматизации генерации объективов.
Ответ 3
Это просто хорошая практика программирования.
Поскольку метки полей записей в Haskell на самом деле являются функциями верхнего уровня, они загрязняют пространство имен модулей. Добавление подчеркивания к метке поля означает, что вы можете определить другую функцию с тем же именем.
Ответ 4
Недавно я обнаружил что-то, имеющее отношение к вашему вопросу.
Я хотел бы контролировать, где создаются экземпляры моего типа, чтобы доказать, что каждое значение действительно. Поэтому я объявил это в своем собственном модуле:
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 }
Тогда, я рассуждал, вне этого модуля, ни один код не мог создать недопустимые Coords.
Я ошибся! x
и y
являются общедоступными, поэтому просто нужно использовать let c = buildCoords 1 1 in c { x = -1 }
для получения недопустимого значения Coords!
Но этот синтаксис возможен только потому, что x и y являются селекторами записи типа. Способ разрешать только допустимые значения:
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 }
Теперь x и y являются просто регулярными функциями. Синтаксис c { x = -1 }
не будет компилироваться, а другие модули не имеют доступа к селектору записи _x.
Проблема была решена с помощью подчеркивания; -)