diff --git a/llvm-extra.cabal b/llvm-extra.cabal
--- a/llvm-extra.cabal
+++ b/llvm-extra.cabal
@@ -1,6 +1,6 @@
 Cabal-Version:  2.2
 Name:           llvm-extra
-Version:        0.10
+Version:        0.10.1
 License:        BSD-3-Clause
 License-File:   LICENSE
 Author:         Henning Thielemann <haskell@henning-thielemann.de>
@@ -61,7 +61,7 @@
   default:     False
 
 Source-Repository this
-  Tag:         0.10
+  Tag:         0.10.1
   Type:        darcs
   Location:    http://code.haskell.org/~thielema/llvm-extra/
 
@@ -101,7 +101,9 @@
     LLVM.Extra.MaybeContinuation
     LLVM.Extra.Either
     LLVM.Extra.Tuple
+    LLVM.Extra.Struct
     LLVM.Extra.Control
+    LLVM.Extra.Function
     LLVM.Extra.Array
     LLVM.Extra.Scalar
     LLVM.Extra.Vector
diff --git a/src/LLVM/Extra/Control.hs b/src/LLVM/Extra/Control.hs
--- a/src/LLVM/Extra/Control.hs
+++ b/src/LLVM/Extra/Control.hs
@@ -363,6 +363,11 @@
 
 -- * return with better type inference
 
+{- |
+'ret' terminates a basic block which interferes badly
+with other control structures in this module.
+If you use the control structures then better use "LLVM.Extra.Function".
+-}
 ret :: Value a -> CodeGenFunction a ()
 ret = LLVM.ret
 
diff --git a/src/LLVM/Extra/Function.hs b/src/LLVM/Extra/Function.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Extra/Function.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE TypeFamilies #-}
+{- |
+Alternative to 'LLVM.Core.defineFunction'
+that creates the final 'LLVM.Core.ret' instruction for you.
+-}
+module LLVM.Extra.Function (
+   C,
+   CodeGen,
+   define,
+   create,
+   createNamed,
+   Return, Result, ret,
+   ) where
+
+import qualified LLVM.Util.Proxy as LP
+import qualified LLVM.Core as LLVM
+
+import Foreign.StablePtr (StablePtr)
+import Foreign.Ptr (Ptr, FunPtr)
+
+import Control.Applicative ((<*>))
+
+import Data.Int (Int8, Int16, Int32, Int64)
+import Data.Word (Word8, Word16, Word32, Word64, Word)
+
+
+define ::
+   (C f) => LLVM.Function f -> CodeGen f -> LLVM.CodeGenModule ()
+define fn body =
+   LLVM.defineFunction fn (addRet (proxyFromElement2 fn) body)
+
+proxyFromElement2 :: f (g a) -> LP.Proxy a
+proxyFromElement2 _ = LP.Proxy
+
+
+create ::
+   (C f) =>
+   LLVM.Linkage -> CodeGen f -> LLVM.CodeGenModule (LLVM.Function f)
+create linkage body = do
+   f <- LLVM.newFunction linkage
+   define f body
+   return f
+
+createNamed ::
+   (C f) =>
+   LLVM.Linkage -> String -> CodeGen f -> LLVM.CodeGenModule (LLVM.Function f)
+createNamed linkage name body = do
+   f <- LLVM.newNamedFunction linkage name
+   define f body
+   return f
+
+
+{- |
+> CodeGen (a->b->...-> IO z) =
+>    Value a -> Value b -> ... CodeGenFunction r (Value z)@.
+-}
+class LLVM.FunctionArgs f => C f where
+   type CodeGen f :: *
+   addRet :: LP.Proxy f -> CodeGen f -> LLVM.FunctionCodeGen f
+
+instance (C b, LLVM.IsFirstClass a) => C (a -> b) where
+   type CodeGen (a -> b) = LLVM.Value a -> CodeGen b
+   addRet proxy f a = addRet (proxy<*>LP.Proxy) (f a)
+
+instance Return a => C (IO a) where
+   type CodeGen (IO a) = LLVM.CodeGenFunction a (Result a)
+   addRet LP.Proxy code = code >>= ret
+
+
+class (LLVM.IsFirstClass a) => Return a where
+   type Result a
+   ret :: Result a -> LLVM.CodeGenFunction a ()
+instance Return () where
+   type Result () = ()
+   ret = LLVM.ret
+
+instance Return Bool where
+   type Result Bool = LLVM.Value Bool; ret = LLVM.ret
+instance Return Int where
+   type Result Int = LLVM.Value Int; ret = LLVM.ret
+instance Return Int8 where
+   type Result Int8 = LLVM.Value Int8; ret = LLVM.ret
+instance Return Int16 where
+   type Result Int16 = LLVM.Value Int16; ret = LLVM.ret
+instance Return Int32 where
+   type Result Int32 = LLVM.Value Int32; ret = LLVM.ret
+instance Return Int64 where
+   type Result Int64 = LLVM.Value Int64; ret = LLVM.ret
+instance Return Word where
+   type Result Word = LLVM.Value Word; ret = LLVM.ret
+instance Return Word8 where
+   type Result Word8 = LLVM.Value Word8; ret = LLVM.ret
+instance Return Word16 where
+   type Result Word16 = LLVM.Value Word16; ret = LLVM.ret
+instance Return Word32 where
+   type Result Word32 = LLVM.Value Word32; ret = LLVM.ret
+instance Return Word64 where
+   type Result Word64 = LLVM.Value Word64; ret = LLVM.ret
+
+instance Return Float where
+   type Result Float = LLVM.Value Float; ret = LLVM.ret
+instance Return Double where
+   type Result Double = LLVM.Value Double; ret = LLVM.ret
+
+instance Return (Ptr a) where
+   type Result (Ptr a) = LLVM.Value (Ptr a); ret = LLVM.ret
+instance (LLVM.IsType a) => Return (LLVM.Ptr a) where
+   type Result (LLVM.Ptr a) = LLVM.Value (LLVM.Ptr a); ret = LLVM.ret
+instance (LLVM.IsFunction a) => Return (FunPtr a) where
+   type Result (FunPtr a) = LLVM.Value (FunPtr a); ret = LLVM.ret
+instance Return (StablePtr a) where
+   type Result (StablePtr a) = LLVM.Value (StablePtr a); ret = LLVM.ret
diff --git a/src/LLVM/Extra/Memory.hs b/src/LLVM/Extra/Memory.hs
--- a/src/LLVM/Extra/Memory.hs
+++ b/src/LLVM/Extra/Memory.hs
@@ -16,15 +16,12 @@
 
 import qualified LLVM.Extra.Multi.Vector as MultiVector
 import qualified LLVM.Extra.Multi.Value as MultiValue
-import qualified LLVM.Extra.ArithmeticPrivate as A
-import qualified LLVM.Extra.Vector as Vector
 import qualified LLVM.Extra.Scalar as Scalar
-import qualified LLVM.Extra.Array as Array
 import qualified LLVM.Extra.Tuple as Tuple
+import qualified LLVM.Extra.Struct as Struct
 import qualified LLVM.Extra.Either as Either
 import qualified LLVM.Extra.Maybe as Maybe
 
-import qualified LLVM.Util.Proxy as LP
 import qualified LLVM.Core as LLVM
 import LLVM.Core
    (getElementPtr0,
@@ -36,17 +33,13 @@
 import qualified Type.Data.Num.Decimal as TypeNum
 import qualified Type.Data.Num.Unary as Unary
 import Type.Data.Num.Decimal (d0, d1, d2, d3)
-import Type.Base.Proxy (Proxy(Proxy), )
-
-import Foreign.StablePtr (StablePtr, )
-import Foreign.Ptr (FunPtr)
+import Type.Base.Proxy (Proxy(Proxy))
 
 import qualified Data.Traversable as Trav
 import qualified Data.Foldable as Fold
 import qualified Data.FixedLength as FixedLength
 import Data.Tuple.HT (fst3, snd3, thd3, )
-import Data.Word (Word8, Word16, Word32, Word64, Word)
-import Data.Int  (Int8,  Int16,  Int32,  Int64, )
+import Data.Word (Word)
 
 import qualified Control.Applicative.HT as App
 import Control.Monad (ap, (<=<))
@@ -237,10 +230,10 @@
       Fold.foldlM
          (\arr (x,i) -> compose x >>= \xc -> LLVM.insertvalue arr xc i)
          (LLVM.value LLVM.undef) $
-      FixedLength.zipWith (,) xs $ iterateTrav (1+) (0::Word64)
+      FixedLength.zipWith (,) xs $ iterateTrav (1+) (0::Word)
    decompose arr =
       Trav.mapM (decompose <=< LLVM.extractvalue arr) $
-      iterateTrav (1+) (0::Word64)
+      iterateTrav (1+) (0::Word)
 
 iterateTrav :: (Applicative t, Trav.Traversable t) => (a -> a) -> a -> t a
 iterateTrav f a0 = snd $ Trav.mapAccumL (\a () -> (f a, a)) a0 $ pure ()
@@ -296,114 +289,45 @@
    compose = return
 
 
-class (LLVM.IsFirstClass llvmType, IsType (Stored llvmType)) =>
-      FirstClass llvmType where
-   type Stored llvmType :: *
-   fromStorable :: Value (Stored llvmType) -> CodeGenFunction r (Value llvmType)
-   toStorable :: Value llvmType -> CodeGenFunction r (Value (Stored llvmType))
-
-instance FirstClass Float  where type Stored Float  = Float  ; fromStorable = return; toStorable = return
-instance FirstClass Double where type Stored Double = Double ; fromStorable = return; toStorable = return
-instance FirstClass Int    where type Stored Int    = Int    ; fromStorable = return; toStorable = return
-instance FirstClass Int8   where type Stored Int8   = Int8   ; fromStorable = return; toStorable = return
-instance FirstClass Int16  where type Stored Int16  = Int16  ; fromStorable = return; toStorable = return
-instance FirstClass Int32  where type Stored Int32  = Int32  ; fromStorable = return; toStorable = return
-instance FirstClass Int64  where type Stored Int64  = Int64  ; fromStorable = return; toStorable = return
-instance FirstClass Word   where type Stored Word   = Word   ; fromStorable = return; toStorable = return
-instance FirstClass Word8  where type Stored Word8  = Word8  ; fromStorable = return; toStorable = return
-instance FirstClass Word16 where type Stored Word16 = Word16 ; fromStorable = return; toStorable = return
-instance FirstClass Word32 where type Stored Word32 = Word32 ; fromStorable = return; toStorable = return
-instance FirstClass Word64 where type Stored Word64 = Word64 ; fromStorable = return; toStorable = return
-instance FirstClass Bool   where
-   type Stored Bool = Word32
-   fromStorable = A.cmp LLVM.CmpNE (LLVM.value LLVM.zero)
-   toStorable = LLVM.zext
-instance
-   (TypeNum.Positive n, LLVM.IsPrimitive a, LLVM.IsPrimitive (Stored a), FirstClass a) =>
-      FirstClass (LLVM.Vector n a) where
-   type Stored (LLVM.Vector n a) = LLVM.Vector n (Stored a)
-   fromStorable = Vector.map fromStorable
-   toStorable = Vector.map toStorable
-instance
-   (TypeNum.Natural n, LLVM.IsFirstClass (Stored a),
-    FirstClass a, IsSized a, IsSized (Stored a)) =>
-      FirstClass (LLVM.Array n a) where
-   type Stored (LLVM.Array n a) = LLVM.Array n (Stored a)
-   fromStorable = Array.map fromStorable
-   toStorable = Array.map toStorable
-
-instance (IsType a) => FirstClass (LLVM.Ptr a) where
-   type Stored (LLVM.Ptr a) = LLVM.Ptr a
-   fromStorable = return; toStorable = return
-instance (LLVM.IsFunction a) => FirstClass (FunPtr a) where
-   type Stored (FunPtr a) = FunPtr a
-   fromStorable = return; toStorable = return
-instance FirstClass (StablePtr a) where
-   type Stored (StablePtr a) = StablePtr a
-   fromStorable = return; toStorable = return
-
+type family StructStruct s
+type instance StructStruct (a,as) = (Struct a, StructStruct as)
+type instance StructStruct () = ()
 
 instance
-   (LLVM.IsFirstClass (LLVM.Struct s),
-    IsType (LLVM.Struct (StoredStruct s)),
-    ConvertStruct s TypeNum.D0 s) =>
-      FirstClass (LLVM.Struct s) where
-   type Stored (LLVM.Struct s) = LLVM.Struct (StoredStruct s)
-   fromStorable sm =
-      case LP.Proxy of
-         sfields -> do
-            s <- decomposeField sfields d0 sm
-            let _ = asTypeOf (fields s) sfields
-            return s
-   toStorable s =
-      composeField (fields s) d0 s
-
-fields :: Value (LLVM.Struct s) -> LP.Proxy s
-fields _ = LP.Proxy
-
-
-type family StoredStruct s :: *
-type instance StoredStruct () = ()
-type instance StoredStruct (s,rem) = (Stored s, StoredStruct rem)
+   (Struct.Phi s, Struct.Undefined s,
+    LLVM.StructFields (StructStruct s),
+    ConvertStruct (StructStruct s) TypeNum.D0 s) =>
+      C (Struct.T s) where
+   type Struct (Struct.T s) = LLVM.Struct (StructStruct s)
+   decompose = fmap Struct.Cons . decomposeFields TypeNum.d0
+   compose (Struct.Cons s) = composeFields TypeNum.d0 s
 
 class ConvertStruct s i rem where
-   decomposeField ::
-      LP.Proxy rem -> Proxy i -> Value (LLVM.Struct (StoredStruct s)) ->
-      CodeGenFunction r (Value (LLVM.Struct s))
-   composeField ::
-      LP.Proxy rem -> Proxy i -> Value (LLVM.Struct s) ->
-      CodeGenFunction r (Value (LLVM.Struct (StoredStruct s)))
+   decomposeFields ::
+      Proxy i -> Value (LLVM.Struct s) -> CodeGenFunction r rem
+   composeFields ::
+      Proxy i -> rem -> CodeGenFunction r (Value (LLVM.Struct s))
 
 instance
-   (sm ~ StoredStruct s,
-    FirstClass a, am ~ Stored a,
-    LLVM.GetValue (LLVM.Struct s) (Proxy i),
-    LLVM.GetValue (LLVM.Struct sm) (Proxy i),
-    LLVM.ValueType (LLVM.Struct s) (Proxy i) ~ a,
-    LLVM.ValueType (LLVM.Struct sm) (Proxy i) ~ am,
+   (TypeNum.Natural i, LLVM.GetField s i, LLVM.FieldType s i ~ Struct a, C a,
     ConvertStruct s (TypeNum.Succ i) rem) =>
       ConvertStruct s i (a,rem) where
-   decomposeField flds i sm = do
-      s <- decomposeField (fmap snd flds) (decSucc i) sm
-      a <- fromStorable =<< LLVM.extractvalue sm i
-      LLVM.insertvalue s a i
-   composeField flds i s = do
-      sm <- composeField (fmap snd flds) (decSucc i) s
-      am <- toStorable =<< LLVM.extractvalue s i
+   decomposeFields i sm =
+      liftA2 (,)
+         (decompose =<< LLVM.extractvalue sm i)
+         (decomposeFields (decSucc i) sm)
+   composeFields i (a,as) = do
+      sm <- composeFields (decSucc i) as
+      am <- compose a
       LLVM.insertvalue sm am i
 
 decSucc :: Proxy n -> Proxy (TypeNum.Succ n)
 decSucc Proxy = Proxy
 
-instance
-   (sm ~ StoredStruct s,
-    IsType (LLVM.Struct s),
-    IsType (LLVM.Struct sm)) =>
-      ConvertStruct s i () where
-   decomposeField _ _ _ =
-      return (LLVM.value LLVM.undef)
-   composeField _ _ _ =
-      return (LLVM.value LLVM.undef)
+instance (LLVM.StructFields s) => ConvertStruct s i () where
+   decomposeFields _ _ = return ()
+   composeFields _ _ = return (LLVM.value LLVM.undef)
+
 
 
 instance (MultiValue.C a, C (Tuple.ValueOf a)) => C (MultiValue.T a) where
diff --git a/src/LLVM/Extra/Multi/Value/Private.hs b/src/LLVM/Extra/Multi/Value/Private.hs
--- a/src/LLVM/Extra/Multi/Value/Private.hs
+++ b/src/LLVM/Extra/Multi/Value/Private.hs
@@ -8,6 +8,7 @@
 import qualified LLVM.Extra.Arithmetic as A
 import qualified LLVM.Extra.Control as C
 import qualified LLVM.Extra.Tuple as Tuple
+import qualified LLVM.Extra.Struct as Struct
 import qualified LLVM.Extra.MaybePrivate as Maybe
 
 import qualified LLVM.Core as LLVM
@@ -32,7 +33,6 @@
 import Data.Complex (Complex((:+)))
 import Data.Tagged (Tagged(Tagged, unTagged))
 import Data.Function (id, (.), ($), )
-import Data.Tuple.HT (uncurry3, )
 import Data.Maybe (Maybe(Nothing,Just), )
 import Data.Bool (Bool(False,True), )
 import Data.Word (Word8, Word16, Word32, Word64, Word)
@@ -445,24 +445,30 @@
 thd3 :: T (a,b,c) -> T c
 thd3 (Cons (_a,_b,c)) = Cons c
 
+curry3 :: (T (a,b,c) -> d) -> (T a -> T b -> T c -> d)
+curry3 f a b c = f $ zip3 a b c
 
+uncurry3 :: (T a -> T b -> T c -> d) -> (T (a,b,c) -> d)
+uncurry3 f = TupleHT.uncurry3 f . unzip3
+
+
 mapFst3 :: (T a0 -> T a1) -> T (a0,b,c) -> T (a1,b,c)
-mapFst3 f = uncurry3 zip3 . TupleHT.mapFst3 f . unzip3
+mapFst3 f = TupleHT.uncurry3 zip3 . TupleHT.mapFst3 f . unzip3
 
 mapSnd3 :: (T b0 -> T b1) -> T (a,b0,c) -> T (a,b1,c)
-mapSnd3 f = uncurry3 zip3 . TupleHT.mapSnd3 f . unzip3
+mapSnd3 f = TupleHT.uncurry3 zip3 . TupleHT.mapSnd3 f . unzip3
 
 mapThd3 :: (T c0 -> T c1) -> T (a,b,c0) -> T (a,b,c1)
-mapThd3 f = uncurry3 zip3 . TupleHT.mapThd3 f . unzip3
+mapThd3 f = TupleHT.uncurry3 zip3 . TupleHT.mapThd3 f . unzip3
 
 mapFst3F :: (Functor f) => (T a0 -> f (T a1)) -> T (a0,b,c) -> f (T (a1,b,c))
-mapFst3F f = fmap (uncurry3 zip3) . FuncHT.mapFst3 f . unzip3
+mapFst3F f = fmap (TupleHT.uncurry3 zip3) . FuncHT.mapFst3 f . unzip3
 
 mapSnd3F :: (Functor f) => (T b0 -> f (T b1)) -> T (a,b0,c) -> f (T (a,b1,c))
-mapSnd3F f = fmap (uncurry3 zip3) . FuncHT.mapSnd3 f . unzip3
+mapSnd3F f = fmap (TupleHT.uncurry3 zip3) . FuncHT.mapSnd3 f . unzip3
 
 mapThd3F :: (Functor f) => (T c0 -> f (T c1)) -> T (a,b,c0) -> f (T (a,b,c1))
-mapThd3F f = fmap (uncurry3 zip3) . FuncHT.mapThd3 f . unzip3
+mapThd3F f = fmap (TupleHT.uncurry3 zip3) . FuncHT.mapThd3 f . unzip3
 
 
 zip :: T a -> T b -> T (a,b)
@@ -498,6 +504,48 @@
 untuple (Cons a) = Cons a
 
 
+class Struct struct where
+   consStruct :: (Struct.T struct ~ a) => a -> T a
+   undefStruct :: (Struct.T struct ~ a) => T a
+   zeroStruct :: (Struct.T struct ~ a) => T a
+   phiStruct :: (Struct.T struct ~ a) =>
+      LLVM.BasicBlock -> T a -> LLVM.CodeGenFunction r (T a)
+   addPhiStruct :: (Struct.T struct ~ a) =>
+      LLVM.BasicBlock -> T a -> T a -> LLVM.CodeGenFunction r ()
+
+instance (Struct struct) => C (Struct.T struct) where
+   cons = consStruct
+   undef = undefStruct
+   zero = zeroStruct
+   phi = phiStruct
+   addPhi = addPhiStruct
+
+instance Struct () where
+   consStruct unit = Cons unit
+   undefStruct = Cons (Struct.Cons ())
+   zeroStruct = Cons (Struct.Cons ())
+   phiStruct _bb = return
+   addPhiStruct _bb _a _b = return ()
+
+structCons :: T a -> T (Struct.T as) -> T (Struct.T (a,as))
+structCons (Cons b) (Cons (Struct.Cons bs)) = Cons (Struct.Cons (b,bs))
+
+structUncons :: T (Struct.T (a,as)) -> (T a, T (Struct.T as))
+structUncons (Cons (Struct.Cons (b,bs))) = (Cons b, Cons (Struct.Cons bs))
+
+instance (C a, Struct as) => Struct (a,as) where
+   consStruct (Struct.Cons (a,as)) =
+      structCons (cons a) (consStruct (Struct.Cons as))
+   undefStruct = structCons undef undefStruct
+   zeroStruct = structCons zero zeroStruct
+   phiStruct bb at =
+      case structUncons at of
+         (a,as) -> Monad.lift2 structCons (phi bb a) (phiStruct bb as)
+   addPhiStruct bb at bt =
+      case (structUncons at, structUncons bt) of
+         ((a,as), (b,bs)) -> addPhi bb a b >> addPhiStruct bb as bs
+
+
 instance C a => C (Tagged tag a) where
    cons = tag . cons . unTagged
    undef = tag undef
@@ -648,7 +696,7 @@
 
 instance (Compose a, Compose b, Compose c) => Compose (a,b,c) where
    type Composed (a,b,c) = (Composed a, Composed b, Composed c)
-   compose = uncurry3 zip3 . TupleHT.mapTriple (compose, compose, compose)
+   compose = TupleHT.uncurry3 zip3 . TupleHT.mapTriple (compose, compose, compose)
 
 instance
    (Decompose pa, Decompose pb, Decompose pc) =>
diff --git a/src/LLVM/Extra/Multi/Vector.hs b/src/LLVM/Extra/Multi/Vector.hs
--- a/src/LLVM/Extra/Multi/Vector.hs
+++ b/src/LLVM/Extra/Multi/Vector.hs
@@ -64,6 +64,8 @@
 
 import qualified Type.Data.Num.Decimal as TypeNum
 
+import qualified Foreign.Storable.Record.Tuple as StoreTuple
+
 import qualified Data.Traversable as Trav
 import qualified Data.NonEmpty.Class as NonEmptyC
 import qualified Data.NonEmpty as NonEmpty
@@ -433,6 +435,23 @@
                (insert k a0 v0)
                (insert k a1 v1)
                (insert k a2 v2)
+
+
+instance (C tuple) => C (StoreTuple.Tuple tuple) where
+   cons = tuple . cons . fmap StoreTuple.getTuple
+   undef = tuple undef
+   zero = tuple zero
+   phi bb = fmap tuple . phi bb . untuple
+   addPhi bb a b = addPhi bb (untuple a) (untuple b)
+   shuffle is u v = fmap tuple $ shuffle is (untuple u) (untuple v)
+   extract k v = fmap MultiValue.tuple $ extract k (untuple v)
+   insert k a v = fmap tuple $ insert k (MultiValue.untuple a) (untuple v)
+
+tuple :: T n tuple -> T n (StoreTuple.Tuple tuple)
+tuple (Cons a) = Cons a
+
+untuple :: T n (StoreTuple.Tuple tuple) -> T n tuple
+untuple (Cons a) = Cons a
 
 
 class (MultiValue.IntegerConstant a, C a) => IntegerConstant a where
diff --git a/src/LLVM/Extra/Storable/Private.hs b/src/LLVM/Extra/Storable/Private.hs
--- a/src/LLVM/Extra/Storable/Private.hs
+++ b/src/LLVM/Extra/Storable/Private.hs
@@ -439,7 +439,7 @@
 update f = MS.StateT $ \a0 -> do a1 <- f a0; return (a0,a1)
 
 advancePtrState ::
-   (C a, Tuple.ValueOf a ~ al, Value (Ptr a) ~ ptr) =>
+   (C a, Value (Ptr a) ~ ptr) =>
    MS.StateT ptr (CodeGenFunction r) ptr
 advancePtrState = update $ advancePtrStatic 1
 
diff --git a/src/LLVM/Extra/Struct.hs b/src/LLVM/Extra/Struct.hs
new file mode 100644
--- /dev/null
+++ b/src/LLVM/Extra/Struct.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleContexts #-}
+{- |
+In contrast to 'LLVM.Struct' it allows to store high-level values
+and thus allows to implement arbitrary-sized tuples of MultiValue's.
+-}
+module LLVM.Extra.Struct where
+
+import qualified LLVM.Extra.Tuple as Tuple
+
+import qualified LLVM.Core as LLVM
+
+import qualified Control.Applicative.HT as App
+import Control.Applicative ((<$>))
+
+
+newtype T struct = Cons struct
+
+
+class Undefined struct where
+   undef :: struct
+
+instance (Undefined struct) => Tuple.Undefined (T struct) where
+   undef = Cons undef
+
+instance
+   (Tuple.Undefined a, Undefined as) =>
+      Undefined (a,as) where
+   undef = (Tuple.undef, undef)
+
+instance Undefined () where
+   undef = ()
+
+
+class Zero struct where
+   zero :: struct
+
+instance (Zero struct) => Tuple.Zero (T struct) where
+   zero = Cons zero
+
+instance (Tuple.Zero a, Zero as) => Zero (a,as) where
+   zero = (Tuple.zero, zero)
+
+instance Zero () where
+   zero = ()
+
+
+class Phi struct where
+   phi :: LLVM.BasicBlock -> struct -> LLVM.CodeGenFunction r struct
+   addPhi :: LLVM.BasicBlock -> struct -> struct -> LLVM.CodeGenFunction r ()
+
+instance (Phi struct) => Tuple.Phi (T struct) where
+   phi bb (Cons s) = Cons <$> phi bb s
+   addPhi bb (Cons a) (Cons b) = addPhi bb a b
+
+instance (Tuple.Phi a, Phi as) => Phi (a,as) where
+   phi bb (a,as) = App.lift2 (,) (Tuple.phi bb a) (phi bb as)
+   addPhi bb (a,as) (b,bs) = Tuple.addPhi bb a b >> addPhi bb as bs
+
+instance Phi () where
+   phi _bb = return
+   addPhi _bb () () = return ()
+
+
+class (Undefined (ValueOf struct)) => Value struct where
+   type ValueOf struct
+   valueOf :: struct -> ValueOf struct
+
+instance (Value struct) => Tuple.Value (T struct) where
+   type ValueOf (T struct) = T (ValueOf struct)
+   valueOf (Cons struct) = Cons $ valueOf struct
+
+instance (Tuple.Value a, Value as) => Value (a,as) where
+   type ValueOf (a,as) = (Tuple.ValueOf a, ValueOf as)
+   valueOf (a,as) = (Tuple.valueOf a, valueOf as)
+
+instance Value () where
+   type ValueOf () = ()
+   valueOf () = ()
