我发现我非常喜欢将GADT与数据类型结合起来,因为它比以前更具有类型安全性(对于大多数用途,几乎与Coq,Agda等人一样好)。遗憾的是,模式匹配在最简单的示例中失败了,我认为除了类型类之外我无法编写我的函数。
这是一个解释我的悲伤的例子:
data Nat = Z | S Nat deriving Eq
data Le :: Nat -> Nat -> * where
Le_base :: Le a a
Le_S :: Le a b -> Le a (S b)
class ReformOp n m where
reform :: Le (S n) (S m) -> Le n m
instance ReformOp a a where
reform Le_base = Le_base
instance ReformOp a b => ReformOp a (S b) where
reform (Le_S p) = Le_S $ reform p
class TransThm a b c where
trans :: Le a b -> Le b c -> Le a c
instance TransThm a a a where
trans = const
instance TransThm a a b => TransThm a a (S b) where
trans Le_base (Le_S p) = Le_S $ trans Le_base p
instance (TransThm a b c, ReformOp b c) => TransThm a (S b) (S c) where
trans (Le_S p) q = Le_S $ trans p $ reform q
我们有2个类型类(一个用于定理,一个用于实用操作)和5个实例 - 仅用于一个简单的定理。理想情况下,Haskell可以查看此函数:
-- not working, I understand why
trans :: Le a b -> Le b c -> Le a c
trans Le_base Le_base = Le_base
trans Le_base (Le_S p) = Le_S $ trans Le_base p
trans (Le_S p) q = Le_S $ trans p $ reform q
并且单独检查每个子句,并且每次调用决定哪些情况是可能的(因此值得尝试匹配),哪些不是,所以当调用trans Le_base Le_base
时Haskell会注意到只有第一种情况允许三个变量相同,并且只尝试匹配第一个子句。
答案 0 :(得分:13)
我不知道trans
的模式匹配定义如何在Agda或Coq中起作用。
如果您改为编写以下内容,则可以:
reform :: Le (S n) (S m) -> Le n m
reform Le_base = Le_base
reform (Le_S Le_base) = Le_S Le_base
reform (Le_S (Le_S p)) = Le_S (reform (Le_S p))
trans :: Le a b -> Le b c -> Le a c
trans Le_base q = q
trans (Le_S p) Le_base = Le_S p
trans (Le_S p) (Le_S q) = Le_S (trans p (reform (Le_S q)))
当然,您还可以更直接地定义:
trans :: Le a b -> Le b c -> Le a c
trans p Le_base = p
trans p (Le_S q) = Le_S (trans p q)