我正在努力掌握GADTs,并且我已经查看了GHC手册中的GADT示例.据我所知,有可能做同样的事情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)
Run Code Online (Sandbox Code Playgroud)
注意,我们有与evalGHC GADTs示例中完全相同的构造函数和完全相同的代码(遍布实例定义).
那么什么是模糊的GADTs呢?有什么我可以做的GADTs,我不能做的MultiParamTypeClasses吗?或者他们只是提供了一种更简洁的方式来做我可以做的事情MultiParamTypeClasses呢?
Dan*_*her 12
您可以方便地将相同类型但具有不同构造函数的GADT值放入容器中,
map eval [Lit 1, If (IsZero (Lit 3)) (Lit 4) (Succ (Lit 6))]
Run Code Online (Sandbox Code Playgroud)
很简单,但要使用不同的类型获得相同的功能,并且具有功能依赖性的MPTC至少是困难的.在Multiparameter类型类方法中,Lit并且If是不同类型的构造函数,因此需要一个包装器类型将它们放入同一个容器中.据我所知,包装类型必须是一个存在类型
data Wrap t = forall a. (IsTerm a t) => Wrapper a
Run Code Online (Sandbox Code Playgroud)
与
instance IsTerm (Wrap t) t where
eval (Wrapper e) = eval e
Run Code Online (Sandbox Code Playgroud)
确保某种类型的安全性以及在列表上的map功能eval.因此,除了方便之外,你还有一半或更多时间回到GADT.
我不确定GADT允许你做什么,没有它们你无法实现,但有些事情会牺牲很多优雅.