{-# LANGUAGE GADTs #-}
data Expr a where
Val :: Num a => a -> Expr a
Eq :: Eq a => Expr a -> Expr a -> Expr Bool
eval :: Expr a -> a
eval (Val x) = x
eval (Eq x y) = (eval x) == (eval y)
instance Show a => Show (Expr a) where
show (Val x) = "Val " ++ (show x)
show (Eq x y) = "Eq (" ++ (show y) ++ ") (" ++ (show x) ++ ")"
失败,出现以下错误消息编译:
Test.hs:13:32: error:
* Could not deduce (Show a1) arising from a use of `show'
from the context: Show a
bound by the instance declaration at test.hs:11:10-32
or from: (a ~ Bool, Eq a1)
bound by a pattern with constructor:
Eq :: forall a. Eq a => Expr a -> Expr a -> Expr Bool,
in an equation for `show'
at test.hs:13:11-16
Possible fix:
add (Show a1) to the context of the data constructor `Eq'
* In the first argument of `(++)', namely `(show y)'
In the second argument of `(++)', namely
`(show y) ++ ") (" ++ (show x) ++ ")"'
In the expression: "Eq (" ++ (show y) ++ ") (" ++ (show x) ++ ")" Failed, modules loaded: none.
注释掉最后一行修复该问题,并检查GHCi中的Expr
类型显示,代替推断Eq
为Eq a => (Expr a) -> (Expr a) -> Expr Bool
类型,GHC实际上推断为Eq a1 => (Expr a1) -> (Expr a1) -> Expr Bool
为 data Expr a where ...
。这解释了错误消息,因为instance Show a => Show (Expr a) where ...
将不会强制a1
成为Show
的实例。
但是我做不是明白,为什么 GHC选择这样做。如果我不得不猜测,我会说这与Eq
返回一个值有关,它根本不依赖于a
,因此GHC“忘记”a
,一旦Eq
返回Expr Bool
。这是 - 至少有点 - 这里发生了什么?
此外,有没有一个干净的解决方案呢?我试了好东西,包括尝试通过InstanceSigs
和ScopedTypeVariables
做这样的事情来“强制”所期望的类型:
instance Show a => Show (Expr a) where
show :: forall a. Eq a => Expr a -> String
show (Eq x y) = "Eq (" ++ (show (x :: Expr a)) ++ ") (" ++ (show (y :: Expr a)) ++ ")"
...
,但没有成功。而且由于我仍然是一名Haskell新手,我甚至不确定,如果这可以以某种方式工作,由于我初步猜测为什么GHC不首先推断“正确”/所需类型。
编辑:
好了,我决定增加一个功能
extract (Eq x _) = x
到文件(没有类型签名),只是为了看看,它会有什么返回类型。 GHC再次拒绝编译,但是这一次,错误消息包含了一些关于skolem类型变量的信息。快速搜索产生了this question,这(我认为?)正式化,我认为这个问题是:一旦Eq :: Expr a -> Expr a -> Expr Bool
返回Expr Bool
,a
“超出范围”。现在我们还没有任何有关a
的信息,因此extract
必须有签名exists a. Expr Bool -> a
,因为forall a. Expr Bool -> a
对于每个a
都不是真的。但是由于GHC不支持exists
,类型检查失败。
最简单的解决方案是从'等式A =>改变等式签名Expr的一个 - > Expr的一个 - > Expr的Bool',这允许'A'在本上下文中是取其数值类型(其可以是或者可以不是的类显示实例),为'(等式一个,显示一个)=> Expr的一个 - > Expr的一个 - > Expr的Bool'比较showable类型的表达式时,这将允许仅仅表达构建当量 – Antisthenes
谢谢,这个作品;但是还有 –
与原来的类型没有很好的解决方案,而在类型构造这种额外需求的解决方案将是有趣的。对于存在类型你可以做的太少了。另一种方法是将'Val'限制为一个固定类型(或者有多个'Val'类构造函数来表示一组有限类型)。 –