GADTs与MultiParamTypeClasses

时间:2012-06-12 10:11:11

标签: haskell gadt

我正在尝试抓住GADTs,我已经查看了GHC手册中的GADTs example。据我所知,可以用MultiParamTypeClasses

做同样的事情
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies,
    FlexibleInstances, UndecidableInstances #-}

class IsTerm a b | a -> b where
  eval :: a -> b

data IntTerm  = Lit Int
              | Succ IntTerm
data BoolTerm = IsZero IntTerm
data If p a   = If p a a
data Pair a b = Pair a b

instance IsTerm IntTerm Int where
  eval (Lit i)      = i
  eval (Succ t)     = 1 + eval t

instance IsTerm BoolTerm Bool where
  eval (IsZero t)   = eval t == 0

instance (IsTerm p Bool, IsTerm a r) => IsTerm (If p a) r where
  eval (If b e1 e2) = if eval b then eval e1 else eval e2

instance (IsTerm a c, IsTerm b d) => IsTerm (Pair a b) (c, d) where
  eval (Pair e1 e2) = (eval e1, eval e2)

请注意,我们拥有与eval完全相同的构造函数和完全相同的代码(与实例定义相交),如GHC GADTs示例中那样。

那么关于GADTs的所有模糊是什么?我可以使用GADTsMultiParamTypeClasses无法做些什么吗?或者他们只是提供了一种更简洁的做事方式,而我可以用MultiParamTypeClasses代替?

2 个答案:

答案 0 :(得分:12)

您可以方便地将相同类型但具有不同构造函数的GADT值放入容器中,

map eval [Lit 1, If (IsZero (Lit 3)) (Lit 4) (Succ (Lit 6))]

是直截了当的,但是使用不同类型获得相同的东西并且具有功能依赖性的MPTC至少是困难的。在Multiparameter类型类方法中,LitIf是不同类型的构造函数,因此需要一个包装器类型将它们放入同一个容器中。据我所知,包装器类型必须是一个存在类型àla

data Wrap t = forall a. (IsTerm a t) => Wrapper a

instance IsTerm (Wrap t) t where
    eval (Wrapper e) = eval e

确保某些类型的安全性以及map在列表中eval等函数的功能。因此,除了方便之外,你还有一半或更多时间回到GADT。

我不确定GADT允许你做什么,没有它们你无法实现,但有些事情会牺牲很多的优雅。

答案 1 :(得分:0)

GADTs只是允许你提供更自然的构造函数定义方法,并且允许在类型级别和构造函数上一起使用匹配(我认为这是你不能没有的东西)。

{-# LANGUAGE GADTs #-}
data Term a = (a ~ Bool) => IsZero (Term Int)
            | (a ~ Int) => Lit Int
eval :: Term a -> a
eval (IsZero t) = eval t == 0
eval (Lit a) = a