
I prefer Bruno's approach, though. It allows meta-level type-checking of expressions and there's the possibility of closing the extension with a wrapper: (References: "Generics as a Library" and his PhD thesis) - GADT as a type class (or encode the type as it's fold): class Exp e where lit :: TyRange a => a -> e a plus :: e Int -> e Int -> e Int and :: e Bool -> e Bool -> e Bool - Notice we cannot construct an ill-typed expression, the Haskell type-checker complains. - |TyRange| is the class of indices: class TyRange a instance TyRange Int instance TyRange Bool - The behaviour is given by instances: newtype Eval a = Eval {eval :: a} instance Exp Eval where lit = Eval plus x y = Eval (eval x + eval y) and x y = Eval (eval x && eval y) Extension is easy: class Exp e => ExpIf e where ifOp :: TyRange a => e Bool -> e a -> e a -> e a instance ExpIf Eval where ifOp c t e = Eval (if (eval c) then (eval t) else (eval e)) class Exp e => ExpMult e where mult :: e Int -> e Int -> e Int instance ExpMult Eval where mult x y = Eval (eval x * eval y) - Adding new meta-level types is easy: instance TyRange a => TyRange [a] instance TyRange Char class Exp e => ExpConcat e where conc :: e [Char] -> e [Char] -> e [Char] instance ExpConcat Eval where conc x y = Eval (eval x ++ eval y) - Closing expressions is also easy: wrap around a type and provide new functions: data TyRange a => Wrap a = Wrap (forall e. (Exp e, ExpIf e, ExpMult e, ExpConcat e) => e a)
evalExp :: TyRange a => Wrap a -> a evalExp (Wrap x) = eval x
- Some expresions: Compare: exp1 :: Exp e => e Int exp1 = plus (lit 3) (lit 3) val1 = eval exp1 With: exp1' :: Eval Int exp1' = plus (lit 3) (lit 3) va1' = eval exp1