是否可以让缺少构造函数的GADT在编译时失败?

时间:2020-03-15 18:49:59

标签: haskell gadt

我才刚刚开始学习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

0 个答案:

没有答案