Могу ли я получить GADT с отсутствующими конструкторами для сбоя во время компиляции? - PullRequest
2 голосов
/ 15 марта 2020

Я только начинаю изучать 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
...