Я только начинаю изучать GADT, и немного разочаровывает, что, если мне не удается включить ключевой конструктор, он все равно компилируется. Есть ли что-то, что я могу сделать, чтобы поймать это во время компиляции?
Это компилирует:
data Expr t where
NumVal :: Num a => a -> Expr a
-- StrVal :: String -> Expr String
Add :: Num a => Expr a -> Expr a -> Expr a
Multiply :: Num a => Expr a -> Expr a -> Expr a
HelloWorld :: Expr String -> Expr String
eval :: Expr t -> t
eval (NumVal n) = n
-- eval (StrVal s) = s
eval (Add e1 e2) = eval e1 + eval e2
eval (Multiply e1 e2) = eval e1 * eval e2
eval (HelloWorld e1) = "Hello " <> eval e1
ghci:
Haskell> eval . HelloWorld . StrVal $ "Nate"
<interactive>:38:21: error:
Data constructor not in scope: StrVal :: [Char] -> Expr String
Чтобы быть честным, позвонив StrVal
вот подсказка, что его не существует, поэтому, пока мой код использует оценщик для GADT, который я экспортирую, я смогу это уловить. Но если моему коду не нужно использовать все аспекты оценщика, похоже, что полнота моего тестового покрытия - единственное, что мешает его доставке.
Моя интуиция подсказывает, что должен быть способ компилятор, чтобы увидеть, что я использую тип Expr String
и что нет способа его создать.
data Expr t where
...
HelloWorld :: Expr String -> Expr String
-
РЕДАКТИРОВАТЬ: В комментариях было отмечено, что я всегда мог предоставьте экземпляр Num
для String
. И действительно, если я это сделаю, все будет в порядке:
Haskell> eval . HelloWorld . NumVal $ "Nate"
"Hello Nate"
Тем не менее, упрощая исходный пример до Int
s проверок типов по-прежнему без способа создания Expr String
.
data Expr t where
IntVal :: Int -> Expr Int
--StrVal :: String -> Expr String
Add :: Expr Int -> Expr Int -> Expr Int
Multiply :: Expr Int -> Expr Int -> Expr Int
HelloWorld :: Expr String -> Expr String
eval :: Expr t -> t
eval (IntVal n) = n
--eval (StrVal s) = s
eval (Add e1 e2) = eval e1 + eval e2
eval (Multiply e1 e2) = eval e1 * eval e2
eval (HelloWorld e1) = "Hello " <> eval e1