instance Typeable a => Data (Expr a) where
    gfoldl _ z (Var v) = z (Var v)
    gfoldl _ z (Lit l) = z (Lit l)
    gfoldl k z (App e1 e2) = z App `k` e1 `k` e2
    gfoldl k z (Lam b e) = z (Lam b) `k` e
    gfoldl k z (Let bs e) = z Let `k` bs `k` e
    gfoldl k z (Case e b t alts) = z (\e' alts' -> Case e' b t (map unwrapAlt alts')) `k` e `k` (map WrappedAlt alts)
    gfoldl k z (Cast e o) = z (\e' -> Cast e' o) `k` e
    gfoldl k z (Note n e) = z (Note n) `k` e
    gfoldl _ z (Type t) = z (Type t)

    gunfold _ _ _ = error "gunfold not defined for Expr"

    toConstr (Var _) = con_Var
    toConstr (Lit _) = con_Lit
    toConstr (App _ _) = con_App
    toConstr (Lam _ _) = con_Lam
    toConstr (Let _ _) = con_Let
    toConstr (Case _ _ _ _) = con_Case
    toConstr (Cast _ _) = con_Cast
    toConstr (Note _ _) = con_Note
    toConstr (Type _) = con_Type
    
    dataTypeOf _ = ty_Expr

con_Var, con_Lit, con_App, con_Lam, con_Let, con_Case, con_Cast, con_Note, con_Type :: Constr
con_Var  = mkConstr ty_Expr "Var"  [] Prefix
con_Lit  = mkConstr ty_Expr "Lit"  [] Prefix
con_App  = mkConstr ty_Expr "App"  [] Prefix
con_Lam  = mkConstr ty_Expr "Lam"  [] Prefix
con_Let  = mkConstr ty_Expr "Let"  [] Prefix
con_Case = mkConstr ty_Expr "Case" [] Prefix
con_Cast = mkConstr ty_Expr "Cast" [] Prefix
con_Note = mkConstr ty_Expr "Note" [] Prefix
con_Type = mkConstr ty_Expr "Type" [] Prefix

ty_Expr :: DataType
ty_Expr = mkDataType "CoreSyn.Expr" [con_Var, con_Lit, con_App, con_Lam, con_Let, con_Case, con_Cast, con_Note, con_Type]


instance Typeable a => Data (Bind a) where
    gfoldl k z (NonRec b e) = z (NonRec b) `k` e
    gfoldl k z (Rec bed_es) = let (bs, es) = unzip bed_es
                              in z (\es' -> Rec (zip bs es')) `k` es

    gunfold _ _ _ = error "gunfold not defined for Bind"

    toConstr (NonRec _ _) = con_NonRec
    toConstr (Rec _)      = con_Rec

    dataTypeOf _ = ty_Bind

con_NonRec, con_Rec :: Constr
con_NonRec = mkConstr ty_Bind "NonRec" [] Prefix
con_Rec    = mkConstr ty_Bind "Rec" [] Prefix

ty_Bind :: DataType
ty_Bind    = mkDataType "CoreSyn.Bind" [con_NonRec, con_Rec]


newtype WrappedAlt a = WrappedAlt (Alt a)

unwrapAlt :: WrappedAlt a -> Alt a
unwrapAlt (WrappedAlt x) = x

instance Typeable a => Typeable (WrappedAlt a) where
    -- Hack or not? I'm not sure!
    typeOf = typeOf . unwrapAlt

instance Typeable a => Data (WrappedAlt a) where
    gfoldl k z (WrappedAlt (con, bs, e)) = z (\e' -> WrappedAlt (con, bs, e')) `k` e

    gunfold _ _ _ = error "gunfold not defined for Alt"

    toConstr _ = con_Alt

    dataTypeOf _ = ty_Alt

con_Alt :: Constr
con_Alt = mkConstr ty_Alt "Alt" [] Prefix

ty_Alt :: DataType
ty_Alt = mkDataType "CoreSyn.Alt" [con_Alt]
