diff --git a/Examples/ALaCarte.hs b/Examples/ALaCarte.hs
--- a/Examples/ALaCarte.hs
+++ b/Examples/ALaCarte.hs
@@ -4,7 +4,7 @@
 {-# LANGUAGE ViewPatterns #-}
 
 -- | Demonstration of the fact that "Language.Syntactic" has the same
--- functionality as /Data types a la carte/ (Wouter Swierstra, in
+-- functionality as /Data types á la carte/ (Wouter Swierstra, in
 -- /Journal of Functional Programming/, 2008)
 
 module ALaCarte where
diff --git a/Examples/MuFeldspar/Core.hs b/Examples/MuFeldspar/Core.hs
--- a/Examples/MuFeldspar/Core.hs
+++ b/Examples/MuFeldspar/Core.hs
@@ -15,6 +15,12 @@
 import qualified Prelude
 import Data.Typeable
 
+import Language.Syntactic
+import Language.Syntactic.Features.Literal
+import Language.Syntactic.Features.PrimFunc
+import Language.Syntactic.Features.Condition
+import Language.Syntactic.Features.Tuple
+import Language.Syntactic.Features.TupleSyntactic
 import Language.Syntactic.Features.Binding
 import Language.Syntactic.Features.Binding.HigherOrder
 
@@ -53,7 +59,7 @@
 
 instance Eval Parallel
   where
-    evaluate Parallel = consEval $ \len ixf -> Prelude.map ixf [0 .. len-1]
+    evaluate Parallel = fromEval $ \len ixf -> Prelude.map ixf [0 .. len-1]
 
 
 
@@ -77,7 +83,7 @@
 
 instance Eval ForLoop
   where
-    evaluate ForLoop = consEval $ \len init body -> foldr body init [0 .. len-1]
+    evaluate ForLoop = fromEval $ \len init body -> foldr body init [0 .. len-1]
 
 
 
@@ -143,7 +149,7 @@
 --------------------------------------------------------------------------------
 
 value :: Syntax a => Internal a -> a
-value = litSyn
+value = sugar . lit
 
 -- | For types containing some kind of \"thunk\", this function can be used to
 -- force computation
@@ -164,8 +170,8 @@
 instance (Type a, Num a) => Num (Data a)
   where
     fromInteger = value . fromInteger
-    abs         = sugarN $ primFunc "abs" abs
-    signum      = sugarN $ primFunc "signum" signum
+    abs         = sugarN $ primFunc1 "abs" abs
+    signum      = sugarN $ primFunc1 "signum" signum
     (+)         = sugarN $ primFunc2 "(+)" (+)
     (-)         = sugarN $ primFunc2 "(-)" (-)
     (*)         = sugarN $ primFunc2 "(*)" (*)
@@ -186,7 +192,7 @@
     :$: lambdaN (desugarN body)
 
 arrLength :: Type a => Data [a] -> Data Length
-arrLength = sugarN $ primFunc "arrLength" Prelude.length
+arrLength = sugarN $ primFunc1 "arrLength" Prelude.length
 
 getIx :: Type a => Data [a] -> Data Index -> Data a
 getIx = sugarN $ primFunc2 "getIx" eval
diff --git a/Language/Syntactic.hs b/Language/Syntactic.hs
--- a/Language/Syntactic.hs
+++ b/Language/Syntactic.hs
@@ -9,10 +9,6 @@
     , module Language.Syntactic.Analysis.Render
     , module Language.Syntactic.Analysis.Evaluation
     , module Language.Syntactic.Analysis.Hash
-    , module Language.Syntactic.Features.Literal
-    , module Language.Syntactic.Features.PrimFunc
-    , module Language.Syntactic.Features.Condition
-    , module Language.Syntactic.Features.Tuple
     , module Language.Syntactic.Features.Annotate
     ) where
 
@@ -23,9 +19,5 @@
 import Language.Syntactic.Analysis.Render
 import Language.Syntactic.Analysis.Evaluation
 import Language.Syntactic.Analysis.Hash
-import Language.Syntactic.Features.Literal
-import Language.Syntactic.Features.PrimFunc
-import Language.Syntactic.Features.Condition
-import Language.Syntactic.Features.Tuple
 import Language.Syntactic.Features.Annotate
 
diff --git a/Language/Syntactic/Features/Annotate.hs b/Language/Syntactic/Features/Annotate.hs
--- a/Language/Syntactic/Features/Annotate.hs
+++ b/Language/Syntactic/Features/Annotate.hs
@@ -27,29 +27,35 @@
 -- 'injectAnn' / 'projectAnn'.
 data Ann info expr a
   where
-    Ann :: info (EvalResult a) -> expr a -> Ann info expr a
+    Ann
+        :: { annInfo :: info (EvalResult a)
+           , annExpr :: expr a
+           }
+        -> Ann info expr a
 
+type AnnSTF info dom a = ASTF (Ann info dom) a
 
 
+
 instance ExprEq expr => ExprEq (Ann info expr)
   where
-    Ann _ a `exprEq` Ann _ b = exprEq a b
+    exprEq a b = annExpr a `exprEq` annExpr b
 
 instance Render expr => Render (Ann info expr)
   where
-    render (Ann _ a) = render a
+    render = render . annExpr
 
 instance ToTree expr => ToTree (Ann info expr)
   where
-    toTreePart args (Ann _ a) = toTreePart args a
+    toTreePart args = toTreePart args . annExpr
 
 instance Eval expr => Eval (Ann info expr)
   where
-    evaluate (Ann _ a) = evaluate a
+    evaluate = evaluate . annExpr
 
 instance ExprHash expr => ExprHash (Ann info expr)
   where
-    exprHash (Ann _ a) = exprHash a
+    exprHash = exprHash . annExpr
 
 
 
diff --git a/Language/Syntactic/Features/Binding.hs b/Language/Syntactic/Features/Binding.hs
--- a/Language/Syntactic/Features/Binding.hs
+++ b/Language/Syntactic/Features/Binding.hs
@@ -190,7 +190,7 @@
 
 instance Eval Let
   where
-    evaluate Let = consEval (flip ($))
+    evaluate Let = fromEval (flip ($))
 
 instance ExprHash Let
   where
diff --git a/Language/Syntactic/Features/Binding/HigherOrder.hs b/Language/Syntactic/Features/Binding/HigherOrder.hs
--- a/Language/Syntactic/Features/Binding/HigherOrder.hs
+++ b/Language/Syntactic/Features/Binding/HigherOrder.hs
@@ -6,8 +6,7 @@
 -- translation.
 
 module Language.Syntactic.Features.Binding.HigherOrder
-    ( module Language.Syntactic
-    , Variable
+    ( Variable
     , evalLambda
     , Let (..)
     , HOLambda (..)
diff --git a/Language/Syntactic/Features/Condition.hs b/Language/Syntactic/Features/Condition.hs
--- a/Language/Syntactic/Features/Condition.hs
+++ b/Language/Syntactic/Features/Condition.hs
@@ -6,11 +6,7 @@
 
 import Data.Hash
 
-import Language.Syntactic.Syntax
-import Language.Syntactic.Analysis.Equality
-import Language.Syntactic.Analysis.Render
-import Language.Syntactic.Analysis.Evaluation
-import Language.Syntactic.Analysis.Hash
+import Language.Syntactic
 
 
 
@@ -30,7 +26,7 @@
 
 instance Eval Condition
   where
-    evaluate Condition = consEval $
+    evaluate Condition = fromEval $
         \cond tHEN eLSE -> if cond then tHEN else eLSE
 
 instance ExprHash Condition
@@ -39,6 +35,7 @@
 
 
 
+-- | Conditional expression
 condition :: (Condition :<: dom, Syntactic a dom) =>
     ASTF dom Bool -> a -> a -> a
 condition cond tHEN eLSE = sugar $ inject Condition
diff --git a/Language/Syntactic/Features/Literal.hs b/Language/Syntactic/Features/Literal.hs
--- a/Language/Syntactic/Features/Literal.hs
+++ b/Language/Syntactic/Features/Literal.hs
@@ -8,11 +8,7 @@
 
 import Data.Hash
 
-import Language.Syntactic.Syntax
-import Language.Syntactic.Analysis.Equality
-import Language.Syntactic.Analysis.Render
-import Language.Syntactic.Analysis.Evaluation
-import Language.Syntactic.Analysis.Hash
+import Language.Syntactic
 
 
 
@@ -34,7 +30,7 @@
 
 instance Eval Literal
   where
-    evaluate (Literal a) = consEval a
+    evaluate (Literal a) = fromEval a
 
 instance ExprHash Literal
   where
@@ -42,15 +38,12 @@
 
 
 
+-- | Literal
 lit :: (Eq a, Show a, Typeable a, Literal :<: dom) => a -> ASTF dom a
 lit = inject . Literal
 
-litSyn
-    :: ( Eq (Internal a)
-       , Show (Internal a)
-       , Syntactic a dom
-       , Literal :<: dom
-       )
-    => Internal a -> a
-litSyn = sugar . inject . Literal
+-- | Annotated literal
+litAnn :: (Eq a, Show a, Typeable a, Literal :<: dom) =>
+    info a -> a -> AnnSTF info dom a
+litAnn info = injectAnn info . Literal
 
diff --git a/Language/Syntactic/Features/PrimFunc.hs b/Language/Syntactic/Features/PrimFunc.hs
--- a/Language/Syntactic/Features/PrimFunc.hs
+++ b/Language/Syntactic/Features/PrimFunc.hs
@@ -8,11 +8,7 @@
 
 import Data.Hash
 
-import Language.Syntactic.Syntax
-import Language.Syntactic.Analysis.Equality
-import Language.Syntactic.Analysis.Render
-import Language.Syntactic.Analysis.Evaluation
-import Language.Syntactic.Analysis.Hash
+import Language.Syntactic
 
 
 
@@ -44,7 +40,7 @@
 
 instance Eval PrimFunc
   where
-    evaluate (PrimFunc _ f) = consEval f
+    evaluate (PrimFunc _ f) = fromEval f
 
 instance ExprHash PrimFunc
   where
@@ -52,14 +48,21 @@
 
 
 
-primFunc :: (Typeable a, PrimFunc :<: dom)
+primFunc1
+    :: ( Typeable a
+       , PrimFunc :<: dom
+       )
     => String
     -> (a -> b)
     -> ASTF dom a
     -> ASTF dom b
-primFunc name f a = inject (PrimFunc name f) :$: a
+primFunc1 name f a = inject (PrimFunc name f) :$: a
 
-primFunc2 :: (Typeable a, Typeable b, PrimFunc :<: dom)
+primFunc2
+    :: ( Typeable a
+       , Typeable b
+       , PrimFunc :<: dom
+       )
     => String
     -> (a -> b -> c)
     -> ASTF dom a
@@ -67,7 +70,12 @@
     -> ASTF dom c
 primFunc2 name f a b = inject (PrimFunc name f) :$: a :$: b
 
-primFunc3 :: (Typeable a, Typeable b, Typeable c, PrimFunc :<: dom)
+primFunc3
+    :: ( Typeable a
+       , Typeable b
+       , Typeable c
+       , PrimFunc :<: dom
+       )
     => String
     -> (a -> b -> c -> d)
     -> ASTF dom a
@@ -76,7 +84,13 @@
     -> ASTF dom d
 primFunc3 name f a b c = inject (PrimFunc name f) :$: a :$: b :$: c
 
-primFunc4 :: (Typeable a, Typeable b, Typeable c, Typeable d, PrimFunc :<: dom)
+primFunc4
+    :: ( Typeable a
+       , Typeable b
+       , Typeable c
+       , Typeable d
+       , PrimFunc :<: dom
+       )
     => String
     -> (a -> b -> c -> d -> e)
     -> ASTF dom a
@@ -85,4 +99,85 @@
     -> ASTF dom d
     -> ASTF dom e
 primFunc4 name f a b c d = inject (PrimFunc name f) :$: a :$: b :$: c :$: d
+
+primFuncAnn1
+    :: ( Typeable a
+       , PrimFunc :<: dom
+       )
+    => String
+    -> (a -> b)
+    -> info b
+    -> AnnSTF info dom a
+    -> AnnSTF info dom b
+primFuncAnn1 name f ib a = injectAnn ib (PrimFunc name f) :$: a
+
+primFuncAnn2
+    :: ( Typeable a
+       , Typeable b
+       , PrimFunc :<: dom
+       )
+    => String
+    -> (a -> b -> c)
+    -> info c
+    -> AnnSTF info dom a
+    -> AnnSTF info dom b
+    -> AnnSTF info dom c
+primFuncAnn2 name f ic a b = injectAnn ic (PrimFunc name f) :$: a :$: b
+
+primFuncAnn3
+    :: ( Typeable a
+       , Typeable b
+       , Typeable c
+       , PrimFunc :<: dom
+       )
+    => String
+    -> (a -> b -> c -> d)
+    -> info d
+    -> AnnSTF info dom a
+    -> AnnSTF info dom b
+    -> AnnSTF info dom c
+    -> AnnSTF info dom d
+primFuncAnn3 name f id a b c =
+    injectAnn id (PrimFunc name f) :$: a :$: b :$: c
+
+primFuncAnn4
+    :: ( Typeable a
+       , Typeable b
+       , Typeable c
+       , Typeable d
+       , PrimFunc :<: dom
+       )
+    => String
+    -> (a -> b -> c -> d -> e)
+    -> info e
+    -> AnnSTF info dom a
+    -> AnnSTF info dom b
+    -> AnnSTF info dom c
+    -> AnnSTF info dom d
+    -> AnnSTF info dom e
+primFuncAnn4 name f ie a b c d =
+    injectAnn ie (PrimFunc name f) :$: a :$: b :$: c :$: d
+
+
+
+-- | Class of expressions that can be treated as primitive functions
+class IsFunction expr
+  where
+    toFunction :: expr a -> PrimFunc a
+
+-- | Default implementation of 'exprEq'
+exprEqFunc :: IsFunction expr => expr a -> expr b -> Bool
+exprEqFunc a b = exprEq (toFunction a) (toFunction b)
+
+-- | Default implementation of 'renderPart'
+renderPartFunc :: IsFunction expr => [String] -> expr a -> String
+renderPartFunc args = renderPart args . toFunction
+
+-- | Default implementation of 'evaluate'
+evaluateFunc :: IsFunction expr => expr a -> a
+evaluateFunc = evaluate . toFunction
+
+-- | Default implementation of 'exprHash'
+exprHashFunc :: IsFunction expr => expr a -> Hash
+exprHashFunc = exprHash . toFunction
 
diff --git a/Language/Syntactic/Features/Tuple.hs b/Language/Syntactic/Features/Tuple.hs
--- a/Language/Syntactic/Features/Tuple.hs
+++ b/Language/Syntactic/Features/Tuple.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE UndecidableInstances #-}
-
 -- | Construction and selection of tuples
 
 module Language.Syntactic.Features.Tuple where
@@ -9,11 +7,8 @@
 import Data.Hash
 import Data.Tuple.Select
 
-import Language.Syntactic.Syntax
-import Language.Syntactic.Analysis.Equality
-import Language.Syntactic.Analysis.Render
-import Language.Syntactic.Analysis.Evaluation
-import Language.Syntactic.Analysis.Hash
+import Language.Syntactic
+import Language.Syntactic.Features.PrimFunc
 
 
 
@@ -27,44 +22,20 @@
     Tup6 :: Tuple (a :-> b :-> c :-> d :-> e :-> f :-> Full (a,b,c,d,e,f))
     Tup7 :: Tuple (a :-> b :-> c :-> d :-> e :-> f :-> g :-> Full (a,b,c,d,e,f,g))
 
-instance ExprEq Tuple
-  where
-    Tup2 `exprEq` Tup2 = True
-    Tup3 `exprEq` Tup3 = True
-    Tup4 `exprEq` Tup4 = True
-    Tup5 `exprEq` Tup5 = True
-    Tup6 `exprEq` Tup6 = True
-    Tup7 `exprEq` Tup7 = True
-    exprEq _ _ = False
-
-instance Render Tuple
-  where
-    render Tup2 = "tup2"
-    render Tup3 = "tup3"
-    render Tup4 = "tup4"
-    render Tup5 = "tup5"
-    render Tup6 = "tup6"
-    render Tup7 = "tup7"
-
-instance ToTree Tuple
-
-instance Eval Tuple
+instance IsFunction Tuple
   where
-    evaluate Tup2 = consEval (,)
-    evaluate Tup3 = consEval (,,)
-    evaluate Tup4 = consEval (,,,)
-    evaluate Tup5 = consEval (,,,,)
-    evaluate Tup6 = consEval (,,,,,)
-    evaluate Tup7 = consEval (,,,,,,)
+    toFunction Tup2 = PrimFunc "tup2" (,)
+    toFunction Tup3 = PrimFunc "tup3" (,,)
+    toFunction Tup4 = PrimFunc "tup4" (,,,)
+    toFunction Tup5 = PrimFunc "tup5" (,,,,)
+    toFunction Tup6 = PrimFunc "tup6" (,,,,,)
+    toFunction Tup7 = PrimFunc "tup7" (,,,,,,)
 
-instance ExprHash Tuple
-  where
-    exprHash Tup2 = hashInt 0
-    exprHash Tup3 = hashInt 1
-    exprHash Tup4 = hashInt 2
-    exprHash Tup5 = hashInt 3
-    exprHash Tup6 = hashInt 4
-    exprHash Tup7 = hashInt 5
+instance ExprEq   Tuple where exprEq     = exprEqFunc
+instance Render   Tuple where renderPart = renderPartFunc
+instance Eval     Tuple where evaluate   = evaluateFunc
+instance ExprHash Tuple where exprHash   = exprHashFunc
+instance ToTree   Tuple
 
 -- | Expressions for selecting elements of a tuple
 data Select a
@@ -77,48 +48,21 @@
     Sel6 :: Sel6 a b => Select (a :-> Full b)
     Sel7 :: Sel7 a b => Select (a :-> Full b)
 
-instance ExprEq Select
-  where
-    Sel1 `exprEq` Sel1 = True
-    Sel2 `exprEq` Sel2 = True
-    Sel3 `exprEq` Sel3 = True
-    Sel4 `exprEq` Sel4 = True
-    Sel5 `exprEq` Sel5 = True
-    Sel6 `exprEq` Sel6 = True
-    Sel7 `exprEq` Sel7 = True
-    exprEq _ _ = False
-
-instance Eval Select
-  where
-    evaluate Sel1 = consEval sel1
-    evaluate Sel2 = consEval sel2
-    evaluate Sel3 = consEval sel3
-    evaluate Sel4 = consEval sel4
-    evaluate Sel5 = consEval sel5
-    evaluate Sel6 = consEval sel6
-    evaluate Sel7 = consEval sel7
-
-instance Render Select
+instance IsFunction Select
   where
-    render Sel1 = "sel1"
-    render Sel2 = "sel2"
-    render Sel3 = "sel3"
-    render Sel4 = "sel4"
-    render Sel5 = "sel5"
-    render Sel6 = "sel6"
-    render Sel7 = "sel7"
-
-instance ToTree Select
+    toFunction Sel1 = PrimFunc "sel1" sel1
+    toFunction Sel2 = PrimFunc "sel2" sel2
+    toFunction Sel3 = PrimFunc "sel3" sel3
+    toFunction Sel4 = PrimFunc "sel4" sel4
+    toFunction Sel5 = PrimFunc "sel5" sel5
+    toFunction Sel6 = PrimFunc "sel6" sel6
+    toFunction Sel7 = PrimFunc "sel7" sel7
 
-instance ExprHash Select
-  where
-    exprHash Sel1 = hashInt 0
-    exprHash Sel2 = hashInt 1
-    exprHash Sel3 = hashInt 2
-    exprHash Sel4 = hashInt 3
-    exprHash Sel5 = hashInt 4
-    exprHash Sel6 = hashInt 5
-    exprHash Sel7 = hashInt 6
+instance ExprEq   Select where exprEq     = exprEqFunc
+instance Render   Select where renderPart = renderPartFunc
+instance Eval     Select where evaluate   = evaluateFunc
+instance ExprHash Select where exprHash   = exprHashFunc
+instance ToTree   Select
 
 -- | Return the selected position, e.g.
 --
@@ -131,198 +75,4 @@
 selectPos Sel5 = 5
 selectPos Sel6 = 6
 selectPos Sel7 = 7
-
-
-
-instance
-    ( Syntactic a dom
-    , Syntactic b dom
-    , Tuple  :<: dom
-    , Select :<: dom
-    ) =>
-      Syntactic (a,b) dom
-  where
-    type Internal (a,b) =
-        ( Internal a
-        , Internal b
-        )
-
-    desugar (a,b) = inject Tup2
-        :$: desugar a
-        :$: desugar b
-
-    sugar a =
-        ( sugar $ inject Sel1 :$: a
-        , sugar $ inject Sel2 :$: a
-        )
-
-instance
-    ( Syntactic a dom
-    , Syntactic b dom
-    , Syntactic c dom
-    , Tuple  :<: dom
-    , Select :<: dom
-    ) =>
-      Syntactic (a,b,c) dom
-  where
-    type Internal (a,b,c) =
-        ( Internal a
-        , Internal b
-        , Internal c
-        )
-
-    desugar (a,b,c) = inject Tup3
-        :$: desugar a
-        :$: desugar b
-        :$: desugar c
-
-    sugar a =
-        ( sugar $ inject Sel1 :$: a
-        , sugar $ inject Sel2 :$: a
-        , sugar $ inject Sel3 :$: a
-        )
-
-instance
-    ( Syntactic a dom
-    , Syntactic b dom
-    , Syntactic c dom
-    , Syntactic d dom
-    , Tuple  :<: dom
-    , Select :<: dom
-    ) =>
-      Syntactic (a,b,c,d) dom
-  where
-    type Internal (a,b,c,d) =
-        ( Internal a
-        , Internal b
-        , Internal c
-        , Internal d
-        )
-
-    desugar (a,b,c,d) = inject Tup4
-        :$: desugar a
-        :$: desugar b
-        :$: desugar c
-        :$: desugar d
-
-    sugar a =
-        ( sugar $ inject Sel1 :$: a
-        , sugar $ inject Sel2 :$: a
-        , sugar $ inject Sel3 :$: a
-        , sugar $ inject Sel4 :$: a
-        )
-
-instance
-    ( Syntactic a dom
-    , Syntactic b dom
-    , Syntactic c dom
-    , Syntactic d dom
-    , Syntactic e dom
-    , Tuple  :<: dom
-    , Select :<: dom
-    ) =>
-      Syntactic (a,b,c,d,e) dom
-  where
-    type Internal (a,b,c,d,e) =
-        ( Internal a
-        , Internal b
-        , Internal c
-        , Internal d
-        , Internal e
-        )
-
-    desugar (a,b,c,d,e) = inject Tup5
-        :$: desugar a
-        :$: desugar b
-        :$: desugar c
-        :$: desugar d
-        :$: desugar e
-
-    sugar a =
-        ( sugar $ inject Sel1 :$: a
-        , sugar $ inject Sel2 :$: a
-        , sugar $ inject Sel3 :$: a
-        , sugar $ inject Sel4 :$: a
-        , sugar $ inject Sel5 :$: a
-        )
-
-instance
-    ( Syntactic a dom
-    , Syntactic b dom
-    , Syntactic c dom
-    , Syntactic d dom
-    , Syntactic e dom
-    , Syntactic f dom
-    , Tuple  :<: dom
-    , Select :<: dom
-    ) =>
-      Syntactic (a,b,c,d,e,f) dom
-  where
-    type Internal (a,b,c,d,e,f) =
-        ( Internal a
-        , Internal b
-        , Internal c
-        , Internal d
-        , Internal e
-        , Internal f
-        )
-
-    desugar (a,b,c,d,e,f) = inject Tup6
-        :$: desugar a
-        :$: desugar b
-        :$: desugar c
-        :$: desugar d
-        :$: desugar e
-        :$: desugar f
-
-    sugar a =
-        ( sugar $ inject Sel1 :$: a
-        , sugar $ inject Sel2 :$: a
-        , sugar $ inject Sel3 :$: a
-        , sugar $ inject Sel4 :$: a
-        , sugar $ inject Sel5 :$: a
-        , sugar $ inject Sel6 :$: a
-        )
-
-instance
-    ( Syntactic a dom
-    , Syntactic b dom
-    , Syntactic c dom
-    , Syntactic d dom
-    , Syntactic e dom
-    , Syntactic f dom
-    , Syntactic g dom
-    , Tuple  :<: dom
-    , Select :<: dom
-    ) =>
-      Syntactic (a,b,c,d,e,f,g) dom
-  where
-    type Internal (a,b,c,d,e,f,g) =
-        ( Internal a
-        , Internal b
-        , Internal c
-        , Internal d
-        , Internal e
-        , Internal f
-        , Internal g
-        )
-
-    desugar (a,b,c,d,e,f,g) = inject Tup7
-        :$: desugar a
-        :$: desugar b
-        :$: desugar c
-        :$: desugar d
-        :$: desugar e
-        :$: desugar f
-        :$: desugar g
-
-    sugar a =
-        ( sugar $ inject Sel1 :$: a
-        , sugar $ inject Sel2 :$: a
-        , sugar $ inject Sel3 :$: a
-        , sugar $ inject Sel4 :$: a
-        , sugar $ inject Sel5 :$: a
-        , sugar $ inject Sel6 :$: a
-        , sugar $ inject Sel7 :$: a
-        )
 
diff --git a/Language/Syntactic/Features/TupleSyntactic.hs b/Language/Syntactic/Features/TupleSyntactic.hs
new file mode 100644
--- /dev/null
+++ b/Language/Syntactic/Features/TupleSyntactic.hs
@@ -0,0 +1,204 @@
+{-# LANGUAGE UndecidableInstances #-}
+
+-- | 'Syntactic' instances for tuples
+module Language.Syntactic.Features.TupleSyntactic where
+
+
+
+import Language.Syntactic.Syntax
+import Language.Syntactic.Features.Tuple
+
+
+
+instance
+    ( Syntactic a dom
+    , Syntactic b dom
+    , Tuple  :<: dom
+    , Select :<: dom
+    ) =>
+      Syntactic (a,b) dom
+  where
+    type Internal (a,b) =
+        ( Internal a
+        , Internal b
+        )
+
+    desugar (a,b) = inject Tup2
+        :$: desugar a
+        :$: desugar b
+
+    sugar a =
+        ( sugar $ inject Sel1 :$: a
+        , sugar $ inject Sel2 :$: a
+        )
+
+instance
+    ( Syntactic a dom
+    , Syntactic b dom
+    , Syntactic c dom
+    , Tuple  :<: dom
+    , Select :<: dom
+    ) =>
+      Syntactic (a,b,c) dom
+  where
+    type Internal (a,b,c) =
+        ( Internal a
+        , Internal b
+        , Internal c
+        )
+
+    desugar (a,b,c) = inject Tup3
+        :$: desugar a
+        :$: desugar b
+        :$: desugar c
+
+    sugar a =
+        ( sugar $ inject Sel1 :$: a
+        , sugar $ inject Sel2 :$: a
+        , sugar $ inject Sel3 :$: a
+        )
+
+instance
+    ( Syntactic a dom
+    , Syntactic b dom
+    , Syntactic c dom
+    , Syntactic d dom
+    , Tuple  :<: dom
+    , Select :<: dom
+    ) =>
+      Syntactic (a,b,c,d) dom
+  where
+    type Internal (a,b,c,d) =
+        ( Internal a
+        , Internal b
+        , Internal c
+        , Internal d
+        )
+
+    desugar (a,b,c,d) = inject Tup4
+        :$: desugar a
+        :$: desugar b
+        :$: desugar c
+        :$: desugar d
+
+    sugar a =
+        ( sugar $ inject Sel1 :$: a
+        , sugar $ inject Sel2 :$: a
+        , sugar $ inject Sel3 :$: a
+        , sugar $ inject Sel4 :$: a
+        )
+
+instance
+    ( Syntactic a dom
+    , Syntactic b dom
+    , Syntactic c dom
+    , Syntactic d dom
+    , Syntactic e dom
+    , Tuple  :<: dom
+    , Select :<: dom
+    ) =>
+      Syntactic (a,b,c,d,e) dom
+  where
+    type Internal (a,b,c,d,e) =
+        ( Internal a
+        , Internal b
+        , Internal c
+        , Internal d
+        , Internal e
+        )
+
+    desugar (a,b,c,d,e) = inject Tup5
+        :$: desugar a
+        :$: desugar b
+        :$: desugar c
+        :$: desugar d
+        :$: desugar e
+
+    sugar a =
+        ( sugar $ inject Sel1 :$: a
+        , sugar $ inject Sel2 :$: a
+        , sugar $ inject Sel3 :$: a
+        , sugar $ inject Sel4 :$: a
+        , sugar $ inject Sel5 :$: a
+        )
+
+instance
+    ( Syntactic a dom
+    , Syntactic b dom
+    , Syntactic c dom
+    , Syntactic d dom
+    , Syntactic e dom
+    , Syntactic f dom
+    , Tuple  :<: dom
+    , Select :<: dom
+    ) =>
+      Syntactic (a,b,c,d,e,f) dom
+  where
+    type Internal (a,b,c,d,e,f) =
+        ( Internal a
+        , Internal b
+        , Internal c
+        , Internal d
+        , Internal e
+        , Internal f
+        )
+
+    desugar (a,b,c,d,e,f) = inject Tup6
+        :$: desugar a
+        :$: desugar b
+        :$: desugar c
+        :$: desugar d
+        :$: desugar e
+        :$: desugar f
+
+    sugar a =
+        ( sugar $ inject Sel1 :$: a
+        , sugar $ inject Sel2 :$: a
+        , sugar $ inject Sel3 :$: a
+        , sugar $ inject Sel4 :$: a
+        , sugar $ inject Sel5 :$: a
+        , sugar $ inject Sel6 :$: a
+        )
+
+instance
+    ( Syntactic a dom
+    , Syntactic b dom
+    , Syntactic c dom
+    , Syntactic d dom
+    , Syntactic e dom
+    , Syntactic f dom
+    , Syntactic g dom
+    , Tuple  :<: dom
+    , Select :<: dom
+    ) =>
+      Syntactic (a,b,c,d,e,f,g) dom
+  where
+    type Internal (a,b,c,d,e,f,g) =
+        ( Internal a
+        , Internal b
+        , Internal c
+        , Internal d
+        , Internal e
+        , Internal f
+        , Internal g
+        )
+
+    desugar (a,b,c,d,e,f,g) = inject Tup7
+        :$: desugar a
+        :$: desugar b
+        :$: desugar c
+        :$: desugar d
+        :$: desugar e
+        :$: desugar f
+        :$: desugar g
+
+    sugar a =
+        ( sugar $ inject Sel1 :$: a
+        , sugar $ inject Sel2 :$: a
+        , sugar $ inject Sel3 :$: a
+        , sugar $ inject Sel4 :$: a
+        , sugar $ inject Sel5 :$: a
+        , sugar $ inject Sel6 :$: a
+        , sugar $ inject Sel7 :$: a
+        )
+
diff --git a/Language/Syntactic/Syntax.hs b/Language/Syntactic/Syntax.hs
--- a/Language/Syntactic/Syntax.hs
+++ b/Language/Syntactic/Syntax.hs
@@ -50,7 +50,7 @@
 -- > conv12 :: (Num2 :<: dom, Add2 :<: dom) => Expr1 a -> ASTF dom a
 -- > conv21 :: (Num2 :<: dom, Add2 :<: dom) => ASTF dom a -> Expr1 a
 --
--- This way of encoding open data types is taken from /Data types a la carte/,
+-- This way of encoding open data types is taken from /Data types à la carte/,
 -- by Wouter Swierstra, in /Journal of Functional Programming/, 2008. However,
 -- we do not need Swierstra's fixed-point machinery for recursive data types.
 -- Instead we rely on 'AST' being recursive.
@@ -59,10 +59,15 @@
     ( -- * Syntax trees
       Full (..)
     , (:->) (..)
+    , HList (..)
     , ConsType
     , ConsEval
     , EvalResult
-    , consEval
+    , fromEval
+    , toEval
+    , listHList
+    , listHListM
+    , mapHList
     , ($:)
     , AST (..)
     , ASTF
@@ -74,8 +79,8 @@
     , resugar
     , SyntacticN (..)
       -- * AST processing
-    , SubTrees (..)
-    , processNode
+    , queryNode
+    , transformNode
     ) where
 
 
@@ -92,8 +97,14 @@
 newtype a :-> b = Partial (a -> b)
   deriving (Typeable)
 
-infixr :->
+-- | Heterogeneous list, indexed by a container type and a 'ConsType'
+data family HList (c :: * -> *) a
 
+data instance HList c (Full a)  = Nil
+data instance HList c (a :-> b) = c (Full a) :*: HList c b
+
+infixr :->, :*:
+
 -- | Fully or partially applied constructor
 --
 -- This class is private to the module to guarantee that all members of the
@@ -109,20 +120,35 @@
   where
     type ConsEval' a
     type EvalResult' a
-    consEval' :: ConsEval' a -> a
 
+    fromEval'   :: ConsEval' a -> a
+    toEval'     :: a -> ConsEval' a
+    listHList'  :: (forall a . c (Full a) -> b) -> HList c a -> [b]
+    listHListM' :: Monad m => (forall a . c (Full a) -> m b) -> HList c a -> m [b]
+    mapHList'   :: (forall a . c1 a -> c2 a) -> HList c1 a -> HList c2 a
+
 instance ConsType' (Full a)
   where
     type ConsEval'   (Full a) = a
     type EvalResult' (Full a) = a
-    consEval' = Full
 
+    fromEval'         = Full
+    toEval'           = result
+    listHList'  f Nil = []
+    listHListM' f Nil = return []
+    mapHList'   f Nil = Nil
+
 instance ConsType' b => ConsType' (a :-> b)
   where
     type ConsEval'   (a :-> b) = a -> ConsEval' b
     type EvalResult' (a :-> b) = EvalResult' b
-    consEval' = Partial . (consEval' .)
 
+    fromEval'                = Partial . (fromEval' .)
+    toEval' (Partial f)      = toEval' . f
+    listHList'  f (a :*: as) = f a : listHList' f as
+    listHListM' f (a :*: as) = sequence (f a : listHList' f as)
+    mapHList'   f (a :*: as) = f a :*: mapHList' f as
+
 -- | Fully or partially applied constructor
 --
 -- This is a public alias for the hidden class 'ConsType''. The only instances
@@ -143,9 +169,27 @@
 type EvalResult a = EvalResult' a
 
 -- | Make a constructor evaluation from a 'ConsEval' representation
-consEval :: ConsType a => ConsEval a -> a
-consEval = consEval'
+fromEval :: ConsType a => ConsEval a -> a
+fromEval = fromEval'
 
+toEval :: ConsType a => a -> ConsEval a
+toEval = toEval'
+
+-- | Convert a heterogeneous list to a normal list
+listHList :: ConsType a =>
+    (forall a . c (Full a) -> b) -> HList c a -> [b]
+listHList = listHList'
+
+-- | Convert a heterogeneous list to a normal list
+listHListM :: (Monad m, ConsType a) =>
+    (forall a . c (Full a) -> m b) -> HList c a -> m [b]
+listHListM = listHListM'
+
+-- | Change the container of each element in a heterogeneous list
+mapHList :: ConsType a =>
+    (forall a . c1 a -> c2 a) -> HList c1 a -> HList c2 a
+mapHList = mapHList'
+
 -- | Semantic constructor application
 ($:) :: (a :-> b) -> a -> b
 Partial f $: a = f a
@@ -282,23 +326,7 @@
 
 
 
--- | Data family for collecting the children of a constructor, for example:
---
--- > subTrees :: forall dom . SubTrees dom (Int :-> Bool :-> Full [Int])
--- > subTrees = a :*: b :*: Nil
--- >   where
--- >     a = undefined :: ASTF dom Int
--- >     b = undefined :: ASTF dom Bool
---
--- @(`SubTrees` a)@ is meaningful iff. @(`ConsType` a)@
-data family SubTrees (dom :: * -> *) a
-
-data instance SubTrees dom (Full a)  = Nil
-data instance SubTrees dom (a :-> b) = ASTF dom a :*: SubTrees dom b
-
-infixr :*:
-
--- | Process an 'AST' using a function that gets direct access to the top-most
+-- | Query an 'AST' using a function that gets direct access to the top-most
 -- constructor and its sub-trees
 --
 -- This function can be used to create 'AST' traversal functions indexed by the
@@ -306,7 +334,7 @@
 --
 -- > class Count subDomain
 -- >   where
--- >     count' :: Count domain => subDomain a -> SubTrees domain a -> Int
+-- >     count' :: Count domain => subDomain a -> HList (AST domain) a -> Int
 -- >
 -- > instance (Count sub1, Count sub2) => Count (sub1 :+: sub2)
 -- >   where
@@ -314,10 +342,10 @@
 -- >     count' (InjectR a) args = count' a args
 -- >
 -- > count :: Count dom => ASTF dom a -> Int
--- > count = processNode count'
+-- > count = queryNode count'
 --
 -- Here, @count@ represents some static analysis on an 'AST'. Each constructor
--- in the tree will be processed by @count'@ indexed by the corresponding symbol
+-- in the tree will be queried by @count'@ indexed by the corresponding symbol
 -- type. That way, @count'@ can be seen as an open-ended function on an open
 -- data type. The @(Count domain)@ constraint on @count'@ is to allow recursion
 -- over sub-trees.
@@ -333,12 +361,28 @@
 -- > instance Count Add
 -- >   where
 -- >     count' Add (a :*: b :*: Nil) = 1 + count a + count b
-processNode :: forall dom a b
-    .  (forall a . ConsType a => dom a -> SubTrees dom a -> b)
+queryNode :: forall dom a b
+    .  (forall a . ConsType a => dom a -> HList (AST dom) a -> b)
     -> ASTF dom a -> b
-processNode f a = process a Nil
+queryNode f a = query a Nil
   where
-    process :: AST dom c -> SubTrees dom c -> b
-    process (Symbol a) args = f a args
-    process (c :$: a)  args = process c (a :*: args)
+    query :: AST dom c -> HList (AST dom) c -> b
+    query (Symbol a) args = f a args
+    query (c :$: a)  args = query c (a :*: args)
+
+
+
+-- | Transform an 'AST' using a function that gets direct access to the top-most
+-- constructor and its sub-trees. This function is similar to 'queryNode', but
+-- returns a transformed 'AST' rather than abstract interpretation.
+transformNode :: forall dom dom' a
+    .  (  forall a . ConsType a
+       => dom a -> HList (AST dom) a -> ASTF dom' (EvalResult a)
+       )
+    -> ASTF dom a -> ASTF dom' a
+transformNode f a = transform a Nil
+  where
+    transform :: AST dom b -> HList (AST dom) b -> ASTF dom' (EvalResult b)
+    transform (Symbol a) args = f a args
+    transform (c :$: a)  args = transform c (a :*: args)
 
diff --git a/syntactic.cabal b/syntactic.cabal
--- a/syntactic.cabal
+++ b/syntactic.cabal
@@ -1,5 +1,5 @@
 Name:           syntactic
-Version:        0.2.0.1
+Version:        0.3
 Synopsis:       Generic abstract syntax, and utilities for embedded languages
 Description:    This library provides:
                 .
@@ -21,7 +21,7 @@
                 languages, such as Feldspar. Currently, it does not support
                 cyclic programs.
                 .
-                \[1\] /Data types a la carte/, by Wouter Swierstra, in
+                \[1\] /Data types à la carte/, by Wouter Swierstra, in
                 /Journal of Functional Programming/, 2008
                 .
                 \[2\] <http://hackage.haskell.org/package/feldspar-language>
@@ -53,14 +53,14 @@
     Language.Syntactic.Analysis.Render
     Language.Syntactic.Analysis.Evaluation
     Language.Syntactic.Analysis.Hash
+    Language.Syntactic.Features.Annotate
     Language.Syntactic.Features.Literal
     Language.Syntactic.Features.PrimFunc
     Language.Syntactic.Features.Condition
     Language.Syntactic.Features.Tuple
-    Language.Syntactic.Features.Annotate
+    Language.Syntactic.Features.TupleSyntactic
     Language.Syntactic.Features.Binding
     Language.Syntactic.Features.Binding.HigherOrder
-
   Other-modules:
 
   Build-depends:
