I'm trying to grasp GADTs
, and I have looked at the GADTs example in GHC's manual. As far as I can tell, it is possible to do the same thing with 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)
Note, that we have the exact same constructors and the exact same code for eval
(spread cross the instance definitions) as in GHC's GADTs
example.
So what's all the fuzz about GADTs
? Is there anything that I can do with GADTs
that I can't do with MultiParamTypeClasses
? Or do they just provide a more concise way of doing things that I could do with MultiParamTypeClasses
instead?
If (Lit 3) (IntTerm 1) (IntTerm 2)
. Consider using ofdata If a = If BoolTerm a a
. – ony