我才刚刚开始学习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
仍然可以进行类型检查,而无法构造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