Ответ 1
Есть ли инструмент [a], который может помочь мне предотвратить этот тип ошибок?
Нет, но может быть.
Как вы знаете, синтаксис записи автоматически генерирует геттеры с тем же именем, что и определяемые вами атрибуты. Поэтому код
data Person = Adult { pName :: String}
| Kid { pName :: String
, pAge :: Int
} deriving Show
создает функции pName :: Person -> String
и pAge :: Person -> Int
. Теперь предположим, что у Хаскелла есть подтипирование. Если это так, то Kid
может быть подтипом Person
, а pAge
может иметь более подходящий тип Kid -> String
. Однако у Haskell нет подтипирования, и поэтому нет типа Kid
.
Теперь, учитывая, что Person -> String
является наиболее конкретным типом, который мы можем дать pAge
, почему бы не предупредить, что pAge
является частичной функцией во время компиляции? Позвольте мне отвлечь вопрос, обратившись к примеру List
data List a = Cons { head :: a, tail :: List a } | Empty
В этом примере head
и tail
являются частичными функциями: двумя компонентами непустого списка, но (из-за отсутствия подтипов Haskell) бессмысленных аксессуаров в пустом списке. Итак, почему нет предупреждения по умолчанию? Ну, по умолчанию вы знаете код, который вы написали. Компилятор не предоставляет предупреждения, если вы используете unsafePerformIO
, потому что вы программист здесь, вы должны использовать такие вещи ответственно.
So tl; dr: если вы хотите получить предупреждение здесь:
getAge :: Person -> Int
getAge p = pAge p
тогда вам не повезло, потому что система типов не имеет достаточной информации, чтобы вывести, что это проблема.
Если вы хотите получить предупреждение здесь:
data Person = Adult | Kid { pAge :: Int }
то я уверен, что было бы тривиально реализовать: просто убедитесь, что данное поле существует в некоторых конструкторах, но не в других. Но я не предвижу, что это предупреждение широко полезно для всех; некоторые могут жаловаться, что это будет просто шум.