diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2015 Alexander Thiemann <mail@athiemann.net>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Alexander Thiemann or agrafix nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,71 @@
+Elm Bridge
+=====
+
+[![Build Status](https://travis-ci.org/agrafix/elm-bridge.svg)](https://travis-ci.org/agrafix/elm-bridge)
+
+[![Hackage Deps](https://img.shields.io/hackage-deps/v/elm-bridge.svg)](http://packdeps.haskellers.com/reverse/elm-bridge)
+
+## Intro
+
+Hackage: [elm-bridge](http://hackage.haskell.org/package/elm-bridge)
+
+Building the bridge from [Haskell](http://haskell.org) to [Elm](http://elm-lang.org) and back. Define types once, use on both sides and enjoy easy (de)serialisation. Cheers!
+
+## Usage
+
+```haskell
+{-# LANGUAGE TemplateHaskell #-}
+import Elm.Derive
+import Elm.Module
+
+import Data.Proxy
+
+data Foo
+   = Foo
+   { f_name :: String
+   , f_blablub :: Int
+   } deriving (Show, Eq)
+
+deriveElmDef defaultOpts ''Foo
+
+main :: IO ()
+main =
+    putStrLn $ makeElmModule "Foo"
+    [ DefineElm (Proxy :: Proxy Foo)
+    ]
+
+```
+
+Output will be:
+
+```elm
+module Foo where
+
+import Json.Decode
+import Json.Decode exposing ((:=))
+import Json.Encode
+
+
+type alias Foo  =
+   { f_name: String
+   , f_blablub: Int
+   }
+
+jsonDecFoo  =
+   ("f_name" := Json.Decode.string) `Json.Decode.andThen` \pf_name ->
+   ("f_blablub" := Json.Decode.int) `Json.Decode.andThen` \pf_blablub ->
+   Json.Decode.succeed {f_name = pf_name, f_blablub = pf_blablub}
+
+jsonEncFoo  val =
+   Json.Encode.object
+   [ ("f_name", Json.Encode.string val.f_name)
+   , ("f_blablub", Json.Encode.int val.f_blablub)
+   ]
+```
+
+For more usage examples check the tests or the examples dir.
+
+## Install
+
+* Using cabal: `cabal install elm-bridge`
+* From Source: `git clone https://github.com/agrafix/elm-bridge.git && cd elm-bridge && cabal install`
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/elm-bridge.cabal b/elm-bridge.cabal
new file mode 100644
--- /dev/null
+++ b/elm-bridge.cabal
@@ -0,0 +1,50 @@
+name:                elm-bridge
+version:             0.1.0.0
+synopsis:            Derive Elm types from Haskell types
+description:         Building the bridge from Haskell to Elm and back. Define types once,
+                     use on both sides and enjoy easy (de)serialisation. Cheers!
+homepage:            http://github.com/agrafix/derive-elm
+license:             BSD3
+license-file:        LICENSE
+author:              Alexander Thiemann <mail@athiemann.net>
+maintainer:          Alexander Thiemann <mail@athiemann.net>
+copyright:           (c) 2015 Alexander Thiemann
+category:            Web, Compiler, Language
+build-type:          Simple
+cabal-version:       >=1.10
+
+extra-source-files:
+    README.md
+    examples/*.hs
+
+library
+  hs-source-dirs:      src
+  exposed-modules:
+                       Elm.Derive
+                       Elm.Json
+                       Elm.Module
+                       Elm.TyRender
+                       Elm.TyRep
+  build-depends:       base >= 4.7 && < 5,
+                       template-haskell
+  default-language:    Haskell2010
+
+test-suite derive-elm-tests
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  other-modules:
+                       Elm.DeriveSpec
+                       Elm.TyRenderSpec
+                       Elm.JsonSpec
+                       Elm.TestHelpers
+                       Elm.ModuleSpec
+  build-depends:
+                       base,
+                       hspec >= 2.0,
+                       elm-bridge
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/agrafix/derive-elm
diff --git a/examples/Example1.hs b/examples/Example1.hs
new file mode 100644
--- /dev/null
+++ b/examples/Example1.hs
@@ -0,0 +1,20 @@
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE TemplateHaskell #-}
+import "elm-bridge" Elm.Derive
+import "elm-bridge" Elm.Module
+
+import Data.Proxy
+
+data Foo
+   = Foo
+   { f_name :: String
+   , f_blablub :: Int
+   } deriving (Show, Eq)
+
+deriveElmDef defaultOpts ''Foo
+
+main :: IO ()
+main =
+    putStrLn $ makeElmModule "Foo"
+    [ DefineElm (Proxy :: Proxy Foo)
+    ]
diff --git a/src/Elm/Derive.hs b/src/Elm/Derive.hs
new file mode 100644
--- /dev/null
+++ b/src/Elm/Derive.hs
@@ -0,0 +1,146 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE TemplateHaskell #-}
+module Elm.Derive
+    ( deriveElmDef, defaultOpts, DeriveOpts(..) )
+where
+
+import Elm.TyRep
+
+import Control.Monad
+import Language.Haskell.TH
+import Language.Haskell.TH.Syntax
+
+data DeriveOpts
+   = DeriveOpts
+   { do_fieldModifier :: String -> String
+   , do_constrModifier :: String -> String
+   }
+
+defaultOpts :: DeriveOpts
+defaultOpts =
+    DeriveOpts
+    { do_fieldModifier = id
+    , do_constrModifier = id
+    }
+
+isConcreteType :: Type -> Bool
+isConcreteType ty =
+    case ty of
+      AppT l r ->
+          isConcreteType l
+      ListT -> True
+
+conCompiler :: String -> String
+conCompiler s =
+    case s of
+      "Double" -> "Float"
+      "Text" -> "String"
+      "Vector" -> "List"
+      _ -> s
+
+compileType :: Type -> Q Exp
+compileType ty =
+    case ty of
+      ListT -> [|ETyCon (ETCon "List")|]
+      TupleT i -> [|ETyTuple i|]
+      ConT name ->
+          let n = conCompiler $ nameBase name
+          in [|ETyCon (ETCon n)|]
+      VarT name ->
+          let n = nameBase name
+          in [|ETyVar (ETVar n)|]
+      SigT ty _ ->
+          compileType ty
+      AppT a b ->
+          let a1 = compileType a
+              b1 = compileType b
+          in [|ETyApp $a1 $b1|]
+      _ -> fail $ "Unsupported type: " ++ show ty
+
+
+runDerive :: Name -> [TyVarBndr] -> (Q Exp -> Q Exp) -> Q [Dec]
+runDerive name vars mkBody =
+    liftM (:[]) elmDefInst
+    where
+      elmDefInst =
+          instanceD (cxt [])
+              (classType `appT` instanceType)
+              [ funD 'compileElmDef
+                         [ clause [ return WildP ] (normalB body) []
+                         ]
+              ]
+
+      classType = conT ''IsElmDefinition
+      instanceType = foldl appT (conT name) $ map varT argNames
+
+      body = mkBody [|ETypeName { et_name = nameStr, et_args = $args }|]
+
+      nameStr = nameBase name
+      args =
+          listE $ map mkTVar argNames
+      mkTVar :: Name -> Q Exp
+      mkTVar n =
+          let str = nameBase n
+          in [|ETVar str|]
+
+      argNames =
+          flip map vars $ \v ->
+              case v of
+                PlainTV tv -> tv
+                KindedTV tv _ -> tv
+
+deriveAlias :: DeriveOpts -> Name -> [TyVarBndr] -> Con -> Q [Dec]
+deriveAlias opts name vars c =
+    case c of
+      RecC _ conFields ->
+          let fields = listE $ map mkField conFields
+          in runDerive name vars $ \typeName ->
+                [|ETypeAlias (EAlias $typeName $fields)|]
+      _ ->
+          fail "Can only derive records like C { v :: Int, w :: a }"
+    where
+      mkField :: VarStrictType -> Q Exp
+      mkField (fname, _, ftype) =
+          [|(fldName, $fldType)|]
+          where
+            fldName = do_fieldModifier opts $ nameBase fname
+            fldType = compileType ftype
+
+deriveSum :: DeriveOpts -> Name -> [TyVarBndr] -> [Con] -> Q [Dec]
+deriveSum opts name vars constrs =
+    runDerive name vars $ \typeName ->
+        [|ETypeSum (ESum $typeName $sumOpts)|]
+    where
+      sumOpts =
+          listE $ map mkOpt constrs
+      mkOpt :: Con -> Q Exp
+      mkOpt c =
+          case c of
+            NormalC name args ->
+                let n = do_constrModifier opts $ nameBase name
+                    tyArgs = listE $ map (\(_, ty) -> compileType ty) args
+                in [|(n, $tyArgs)|]
+            _ ->
+                fail "Can only derive sum types with options like C Int a"
+
+deriveSynonym :: DeriveOpts -> Name -> [TyVarBndr] -> Type -> Q [Dec]
+deriveSynonym opts name vars otherT =
+    runDerive name vars $ \typeName ->
+        [|ETypePrimAlias (EPrimAlias $typeName $otherType)|]
+    where
+      otherType = compileType otherT
+
+deriveElmDef :: DeriveOpts -> Name -> Q [Dec]
+deriveElmDef opts name =
+    do TyConI tyCon <- reify name
+       case tyCon of
+         DataD _ _ tyVars constrs _ ->
+             case constrs of
+               [] -> fail "Can not derive empty data decls"
+               [x] -> deriveAlias opts name tyVars x
+               _ -> deriveSum opts name tyVars constrs
+         NewtypeD _ _ tyVars constr _ ->
+             deriveAlias opts name tyVars constr
+         TySynD _ vars otherTy ->
+             deriveSynonym opts name vars otherTy
+         _ -> fail "Oops, can only derive data and newtype"
diff --git a/src/Elm/Json.hs b/src/Elm/Json.hs
new file mode 100644
--- /dev/null
+++ b/src/Elm/Json.hs
@@ -0,0 +1,133 @@
+{- |
+This module implements a generator for JSON serialisers and parsers of arbitrary elm types.
+Please note: It's still very hacky and might not work for all possible elm types yet.
+-}
+module Elm.Json
+    ( jsonParserForDef
+    , jsonSerForDef
+    )
+where
+
+import Data.List
+import Data.Maybe
+
+import Elm.TyRep
+
+-- | Compile a JSON parser for an Elm type
+jsonParserForType :: EType -> String
+jsonParserForType ty =
+    case ty of
+      ETyVar (ETVar v) -> "localDecoder_" ++ v
+      ETyCon (ETCon "Int") -> "Json.Decode.int"
+      ETyCon (ETCon "Float") -> "Json.Decode.float"
+      ETyCon (ETCon "String") -> "Json.Decode.string"
+      ETyCon (ETCon "Bool") -> "Json.Decode.bool"
+      ETyCon (ETCon c) -> "jsonDec" ++ c
+      ETyApp (ETyCon (ETCon "List")) t' -> "Json.Decode.list (" ++ jsonParserForType t' ++ ")"
+      ETyApp (ETyCon (ETCon "Maybe")) t' -> "Json.Decode.maybe (" ++ jsonParserForType t' ++ ")"
+      _ ->
+          case unpackTupleType ty of
+            [] -> error $ "This should never happen. Failed to unpackTupleType: " ++ show ty
+            [x] ->
+                case unpackToplevelConstr x of
+                  (y : ys) ->
+                      jsonParserForType y ++ " "
+                      ++ unwords (catMaybes $ map (\t' ->
+                                                 case t' of
+                                                   ETyVar _ -> Just $ "(" ++ jsonParserForType t' ++ ")"
+                                                   _ -> Nothing
+                                            ) ys)
+                  _ -> error $ "Do suitable json parser found for " ++ show ty
+            xs ->
+                let tupleLen = length xs
+                    commas = replicate (tupleLen - 1) ','
+                in "Json.Decode.tuple" ++ show tupleLen ++ " (" ++ commas ++ ") "
+                    ++ unwords (map (\t' -> "(" ++ jsonParserForType t' ++ ")") xs)
+
+-- | Compile a JSON parser for an Elm type definition
+jsonParserForDef :: ETypeDef -> String
+jsonParserForDef etd =
+    case etd of
+      ETypePrimAlias (EPrimAlias name ty) ->
+          makeName name ++  " = " ++ jsonParserForType ty ++ "\n"
+      ETypeAlias (EAlias name fields) ->
+          makeName name ++ " = \n"
+          ++ intercalate "\n" (map (\(fldName, fldType) -> "   (\"" ++ fldName ++ "\" := "
+                                    ++ jsonParserForType fldType
+                                    ++ ") `Json.Decode.andThen` \\p" ++ fldName ++ " -> ") fields)
+          ++ "\n   Json.Decode.succeed {" ++ intercalate ", " (map (\(fldName, _) -> fldName ++ " = p" ++ fldName) fields) ++ "}\n"
+      ETypeSum (ESum name opts) ->
+          makeName name ++ " = \n"
+          ++ "   Json.Decode.oneOf \n   [ "
+          ++ intercalate "\n   , " (map mkOpt opts) ++ "\n"
+          ++ "   ]\n"
+          where
+            mkOpt (name, args) =
+                let argLen = length args
+                in "(\"" ++ name ++ "\" := Json.tuple" ++ show argLen ++ " " ++ name ++ " "
+                   ++ unwords (map (\t' -> "(" ++ jsonParserForType t' ++ ")") args)
+                   ++ ")"
+    where
+      makeName name =
+           "jsonDec" ++ et_name name ++ " "
+           ++ unwords (map (\tv -> "localDecoder_" ++ tv_name tv) $ et_args name)
+
+-- | Compile a JSON serializer for an Elm type
+jsonSerForType :: EType -> String
+jsonSerForType ty =
+    case ty of
+      ETyVar (ETVar v) -> "localEncoder_" ++ v
+      ETyCon (ETCon "Int") -> "Json.Encode.int"
+      ETyCon (ETCon "Float") -> "Json.Encode.float"
+      ETyCon (ETCon "String") -> "Json.Encode.string"
+      ETyCon (ETCon "Bool") -> "Json.Encode.bool"
+      ETyCon (ETCon c) -> "jsonEnc" ++ c
+      ETyApp (ETyCon (ETCon "List")) t' -> "(Json.Encode.list << map " ++ jsonSerForType t' ++ ")"
+      ETyApp (ETyCon (ETCon "Maybe")) t' -> "(\v -> case v of Just val -> " ++ jsonSerForType t' ++ " val Nothing -> Json.Encode.null)"
+      _ ->
+          case unpackTupleType ty of
+            [] -> error $ "This should never happen. Failed to unpackTupleType: " ++ show ty
+            [x] ->
+                case unpackToplevelConstr x of
+                  (y : ys) ->
+                      "(" ++ jsonSerForType y ++ " "
+                      ++ unwords (catMaybes $ map (\t' ->
+                                                 case t' of
+                                                   ETyVar _ -> Just $ "(" ++ jsonSerForType t' ++ ")"
+                                                   _ -> Nothing
+                                            ) ys) ++ ")"
+                  _ -> error $ "Do suitable json serialiser found for " ++ show ty
+            xs ->
+                let tupleLen = length xs
+                    tupleArgsV = zip xs [1..]
+                    tupleArgs =
+                        unwords $ map (\(_, v) -> "v" ++ show v) tupleArgsV
+                in "(\\" ++ tupleArgs ++ " -> [" ++  intercalate "," (map (\(t', idx) -> "(" ++ jsonSerForType t' ++ ") v" ++ show idx) tupleArgsV) ++ "]"
+
+-- | Compile a JSON serializer for an Elm type definition
+jsonSerForDef :: ETypeDef -> String
+jsonSerForDef etd =
+    case etd of
+      ETypePrimAlias (EPrimAlias name ty) ->
+          makeName name ++  " = " ++ jsonSerForType ty ++ " val\n"
+      ETypeAlias (EAlias name fields) ->
+          makeName name ++ " = \n   Json.Encode.object\n   ["
+          ++ intercalate "\n   ," (map (\(fldName, fldType) -> " (\"" ++ fldName ++ "\", " ++ jsonSerForType fldType ++ " val." ++ fldName ++ ")") fields)
+          ++ "\n   ]\n"
+      ETypeSum (ESum name opts) ->
+          makeName name ++ " = \n"
+          ++ "   case val of\n   "
+          ++ intercalate "\n   " (map mkOpt opts) ++ "\n"
+          where
+            mkOpt (name, args) =
+                let namedArgs = zip args [1..]
+                    argList = unwords $ map (\(_, i) -> "v" ++ show i ) namedArgs
+                    mkArg :: (EType, Int) -> String
+                    mkArg (arg, idx) =
+                        jsonSerForType arg ++ " v" ++ show idx
+                in "   " ++ name ++ " " ++ argList ++ " -> [" ++ intercalate ", " (map mkArg namedArgs) ++ "]"
+    where
+      makeName name =
+           "jsonEnc" ++ et_name name ++ " "
+           ++ unwords (map (\tv -> "localEncoder_" ++ tv_name tv) $ et_args name)
+           ++ " val"
diff --git a/src/Elm/Module.hs b/src/Elm/Module.hs
new file mode 100644
--- /dev/null
+++ b/src/Elm/Module.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Elm.Module where
+
+import Data.Proxy
+import Data.List
+
+import Elm.TyRep
+import Elm.TyRender
+import Elm.Json
+
+-- | Existential quantification wrapper for lists of type definitions
+data DefineElm
+   = forall a. IsElmDefinition a => DefineElm (Proxy a)
+
+-- | Compile an Elm module
+makeElmModule :: String -> [DefineElm] -> String
+makeElmModule moduleName defs =
+    "module " ++ moduleName ++ " where \n\n"
+    ++ "import Json.Decode\n"
+    ++ "import Json.Decode exposing ((:=))\n"
+    ++ "import Json.Encode\n"
+    ++ "\n\n"
+    ++ intercalate "\n\n" (map mkDef defs)
+    where
+      mkDef (DefineElm proxy) =
+          let def = compileElmDef proxy
+          in renderElm def ++ "\n" ++ jsonParserForDef def ++ "\n" ++ jsonSerForDef def ++ "\n"
diff --git a/src/Elm/TyRender.hs b/src/Elm/TyRender.hs
new file mode 100644
--- /dev/null
+++ b/src/Elm/TyRender.hs
@@ -0,0 +1,57 @@
+module Elm.TyRender where
+
+import Elm.TyRep
+
+import Data.List
+
+class ElmRenderable a where
+    renderElm :: a -> String
+
+instance ElmRenderable ETypeDef where
+    renderElm td =
+        case td of
+          ETypeAlias alias -> renderElm alias
+          ETypeSum s -> renderElm s
+          ETypePrimAlias pa -> renderElm pa
+
+instance ElmRenderable EType where
+    renderElm ty =
+        case unpackTupleType ty of
+          [t] -> renderSingleTy t
+          xs -> "(" ++ intercalate ", " (map renderSingleTy xs) ++ ")"
+        where
+          renderSingleTy ty =
+              case ty of
+                ETyVar v -> renderElm v
+                ETyCon c -> renderElm c
+                ETyTuple n -> error "Library Bug: This should never happen!"
+                ETyApp l r -> "(" ++ renderElm l ++ " " ++ renderElm r ++ ")"
+
+instance ElmRenderable ETCon where
+    renderElm = tc_name
+
+instance ElmRenderable ETVar where
+    renderElm = tv_name
+
+instance ElmRenderable ETypeName where
+    renderElm tyName =
+        et_name tyName ++ " " ++ unwords (map renderElm $ et_args tyName)
+
+instance ElmRenderable EAlias where
+    renderElm alias =
+        "type alias " ++ renderElm (ea_name alias) ++ " = \n   { "
+        ++ intercalate "\n   , " (map (\(fld, ty) -> fld ++ ": " ++ renderElm ty) (ea_fields alias))
+        ++ "\n   }\n"
+
+instance ElmRenderable ESum where
+    renderElm s =
+        "type " ++ renderElm (es_name s) ++ " = \n    "
+        ++ intercalate "\n    | " (map mkOpt (es_options s))
+        ++ "\n"
+        where
+          mkOpt (name, types) =
+              name ++ " " ++ unwords (map renderElm types)
+
+instance ElmRenderable EPrimAlias where
+    renderElm pa =
+        "type alias " ++ renderElm (epa_name pa) ++ " = " ++ renderElm (epa_type pa) ++ "\n"
diff --git a/src/Elm/TyRep.hs b/src/Elm/TyRep.hs
new file mode 100644
--- /dev/null
+++ b/src/Elm/TyRep.hs
@@ -0,0 +1,79 @@
+module Elm.TyRep where
+
+import Data.List
+import Data.Proxy
+
+class IsElmDefinition a where
+    compileElmDef :: Proxy a -> ETypeDef
+
+data ETypeDef
+   = ETypeAlias EAlias
+   | ETypePrimAlias EPrimAlias
+   | ETypeSum ESum
+     deriving (Show, Eq)
+
+data EType
+   = ETyVar ETVar
+   | ETyCon ETCon
+   | ETyApp EType EType
+   | ETyTuple Int
+   deriving (Show, Eq, Ord)
+
+data ETCon
+   = ETCon
+   { tc_name :: String
+   } deriving (Show, Eq, Ord)
+
+data ETVar
+   = ETVar
+   { tv_name :: String
+   } deriving (Show, Eq, Ord)
+
+data ETypeName
+   = ETypeName
+   { et_name :: String
+   , et_args :: [ETVar]
+   } deriving (Show, Eq, Ord)
+
+data EPrimAlias
+   = EPrimAlias
+   { epa_name :: ETypeName
+   , epa_type :: EType
+   } deriving (Show, Eq, Ord)
+
+data EAlias
+   = EAlias
+   { ea_name :: ETypeName
+   , ea_fields :: [(String, EType)]
+   } deriving (Show, Eq, Ord)
+
+data ESum
+   = ESum
+   { es_name :: ETypeName
+   , es_options :: [(String, [EType])]
+   } deriving (Show, Eq, Ord)
+
+unpackTupleType :: EType -> [EType]
+unpackTupleType t =
+    unfoldr (\ty ->
+                 case ty of
+                   Just (ETyApp (ETyApp (ETyTuple i) r) r') ->
+                       Just (r, Just r')
+                   Just t ->
+                       Just (t, Nothing)
+                   Nothing ->
+                       Nothing
+            ) (Just t)
+
+unpackToplevelConstr :: EType -> [EType]
+unpackToplevelConstr t =
+    reverse $
+    flip unfoldr (Just t) $ \mT ->
+        case mT of
+          Nothing -> Nothing
+          Just t' ->
+              case t' of
+                ETyApp l r ->
+                    Just (r, Just l)
+                _ ->
+                    Just (t', Nothing)
diff --git a/test/Elm/DeriveSpec.hs b/test/Elm/DeriveSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Elm/DeriveSpec.hs
@@ -0,0 +1,83 @@
+{-# LANGUAGE TemplateHaskell #-}
+module Elm.DeriveSpec (spec) where
+
+import Elm.Derive
+import Elm.TyRep
+
+import Data.Proxy
+import Test.Hspec
+
+data Foo
+   = Foo
+   { f_name :: String
+   , f_blablub :: Int
+   } deriving (Show, Eq)
+
+data Bar a
+   = Bar
+   { b_name :: a
+   , b_blablub :: Int
+   , b_tuple :: (Int, String)
+   , b_list :: [Bool]
+   } deriving (Show, Eq)
+
+data SomeOpts a
+   = Okay Int
+   | NotOkay a
+
+deriveElmDef defaultOpts ''Foo
+deriveElmDef defaultOpts ''Bar
+deriveElmDef defaultOpts ''SomeOpts
+
+fooElm :: ETypeDef
+fooElm =
+    ETypeAlias $
+    EAlias
+    { ea_name =
+          ETypeName
+          { et_name = "Foo"
+          , et_args = []
+          }
+    , ea_fields =
+        [("f_name",ETyCon (ETCon {tc_name = "String"})),("f_blablub",ETyCon (ETCon {tc_name = "Int"}))]
+    }
+
+barElm :: ETypeDef
+barElm =
+    ETypeAlias $
+    EAlias
+    { ea_name =
+          ETypeName
+          { et_name = "Bar"
+          , et_args = [ETVar {tv_name = "a"}]
+          }
+    , ea_fields =
+        [ ("b_name",ETyVar (ETVar {tv_name = "a"}))
+        , ("b_blablub",ETyCon (ETCon {tc_name = "Int"}))
+        , ("b_tuple",ETyApp (ETyApp (ETyTuple 2) (ETyCon (ETCon {tc_name = "Int"}))) (ETyCon (ETCon {tc_name = "String"})))
+        , ("b_list",ETyApp (ETyCon (ETCon {tc_name = "List"})) (ETyCon (ETCon {tc_name = "Bool"})))
+        ]
+    }
+
+someOptsElm :: ETypeDef
+someOptsElm =
+    ETypeSum $
+    ESum
+    { es_name =
+          ETypeName
+          { et_name = "SomeOpts"
+          , et_args = [ETVar {tv_name = "a"}]
+          }
+    , es_options =
+        [ ("Okay",[ETyCon (ETCon {tc_name = "Int"})])
+        , ("NotOkay",[ETyVar (ETVar {tv_name = "a"})])
+        ]
+    }
+
+spec :: Spec
+spec =
+    describe "deriveElmRep" $
+    it "should produce the correct types" $
+    do compileElmDef (Proxy :: Proxy Foo) `shouldBe` fooElm
+       compileElmDef (Proxy :: Proxy (Bar a)) `shouldBe` barElm
+       compileElmDef (Proxy :: Proxy (SomeOpts a)) `shouldBe` someOptsElm
diff --git a/test/Elm/JsonSpec.hs b/test/Elm/JsonSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Elm/JsonSpec.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE TemplateHaskell #-}
+module Elm.JsonSpec (spec) where
+
+import Elm.Derive
+import Elm.TyRep
+import Elm.Json
+
+import Elm.TestHelpers
+
+import Data.Proxy
+import Test.Hspec
+
+data Foo
+   = Foo
+   { f_name :: String
+   , f_blablub :: Int
+   } deriving (Show, Eq)
+
+data Bar a
+   = Bar
+   { b_name :: a
+   , b_blablub :: Int
+   , b_tuple :: (Int, String)
+   , b_list :: [Bool]
+   } deriving (Show, Eq)
+
+data SomeOpts a
+   = Okay Int
+   | NotOkay a
+
+$(deriveElmDef (fieldDropOpts 2) ''Foo)
+$(deriveElmDef (fieldDropOpts 2) ''Bar)
+$(deriveElmDef defaultOpts ''SomeOpts)
+
+fooSer :: String
+fooSer = "jsonEncFoo  val = \n   Json.Encode.object\n   [ (\"name\", Json.Encode.string val.name)\n   , (\"blablub\", Json.Encode.int val.blablub)\n   ]\n"
+
+fooParse :: String
+fooParse = "jsonDecFoo  = \n   (\"name\" := Json.Decode.string) `Json.Decode.andThen` \\pname -> \n   (\"blablub\" := Json.Decode.int) `Json.Decode.andThen` \\pblablub -> \n   Json.Decode.succeed {name = pname, blablub = pblablub}\n"
+
+barSer :: String
+barSer = "jsonEncBar localEncoder_a val = \n   Json.Encode.object\n   [ (\"name\", localEncoder_a val.name)\n   , (\"blablub\", Json.Encode.int val.blablub)\n   , (\"tuple\", (\\v1 v2 -> [(Json.Encode.int) v1,(Json.Encode.string) v2] val.tuple)\n   , (\"list\", (Json.Encode.list << map Json.Encode.bool) val.list)\n   ]\n"
+
+barParse :: String
+barParse = "jsonDecBar localDecoder_a = \n   (\"name\" := localDecoder_a) `Json.Decode.andThen` \\pname -> \n   (\"blablub\" := Json.Decode.int) `Json.Decode.andThen` \\pblablub -> \n   (\"tuple\" := Json.Decode.tuple2 (,) (Json.Decode.int) (Json.Decode.string)) `Json.Decode.andThen` \\ptuple -> \n   (\"list\" := Json.Decode.list (Json.Decode.bool)) `Json.Decode.andThen` \\plist -> \n   Json.Decode.succeed {name = pname, blablub = pblablub, tuple = ptuple, list = plist}\n"
+
+someOptsParse :: String
+someOptsParse = "jsonDecSomeOpts localDecoder_a = \n   Json.Decode.oneOf \n   [ (\"Okay\" := Json.tuple1 Okay (Json.Decode.int))\n   , (\"NotOkay\" := Json.tuple1 NotOkay (localDecoder_a))\n   ]\n"
+
+someOptsSer :: String
+someOptsSer = "jsonEncSomeOpts localEncoder_a val = \n   case val of\n      Okay v1 -> [Json.Encode.int v1]\n      NotOkay v1 -> [localEncoder_a v1]\n"
+
+spec :: Spec
+spec =
+    describe "json serialisation" $
+    do let rFoo = compileElmDef (Proxy :: Proxy Foo)
+           rBar = compileElmDef (Proxy :: Proxy (Bar a))
+           rSomeOpts = compileElmDef (Proxy :: Proxy (SomeOpts a))
+       it "should produce the correct ser code" $
+          do jsonSerForDef rFoo `shouldBe` fooSer
+             jsonSerForDef rBar `shouldBe` barSer
+             jsonSerForDef rSomeOpts `shouldBe` someOptsSer
+       it "should produce the correct parse code" $
+          do jsonParserForDef rFoo `shouldBe` fooParse
+             jsonParserForDef rBar `shouldBe` barParse
+             jsonParserForDef rSomeOpts `shouldBe` someOptsParse
diff --git a/test/Elm/ModuleSpec.hs b/test/Elm/ModuleSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Elm/ModuleSpec.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE TemplateHaskell #-}
+module Elm.ModuleSpec (spec) where
+
+import Elm.Derive
+import Elm.Module
+
+import Elm.TestHelpers
+
+import Data.Proxy
+import Test.Hspec
+
+data Bar a
+   = Bar
+   { b_name :: a
+   , b_blablub :: Int
+   , b_tuple :: (Int, String)
+   , b_list :: [Bool]
+   } deriving (Show, Eq)
+
+$(deriveElmDef (fieldDropOpts 2) ''Bar)
+
+moduleCode :: String
+moduleCode = "module Foo where \n\nimport Json.Decode\nimport Json.Decode exposing ((:=))\nimport Json.Encode\n\n\ntype alias Bar a = \n   { name: a\n   , blablub: Int\n   , tuple: (Int, String)\n   , list: (List Bool)\n   }\n\njsonDecBar localDecoder_a = \n   (\"name\" := localDecoder_a) `Json.Decode.andThen` \\pname -> \n   (\"blablub\" := Json.Decode.int) `Json.Decode.andThen` \\pblablub -> \n   (\"tuple\" := Json.Decode.tuple2 (,) (Json.Decode.int) (Json.Decode.string)) `Json.Decode.andThen` \\ptuple -> \n   (\"list\" := Json.Decode.list (Json.Decode.bool)) `Json.Decode.andThen` \\plist -> \n   Json.Decode.succeed {name = pname, blablub = pblablub, tuple = ptuple, list = plist}\n\njsonEncBar localEncoder_a val = \n   Json.Encode.object\n   [ (\"name\", localEncoder_a val.name)\n   , (\"blablub\", Json.Encode.int val.blablub)\n   , (\"tuple\", (\\v1 v2 -> [(Json.Encode.int) v1,(Json.Encode.string) v2] val.tuple)\n   , (\"list\", (Json.Encode.list << map Json.Encode.bool) val.list)\n   ]\n\n"
+
+spec :: Spec
+spec =
+    describe "makeElmModule" $
+    it "should produce the correct code" $
+       do let modu =
+                 makeElmModule "Foo" [DefineElm (Proxy :: Proxy (Bar a))]
+          modu `shouldBe` moduleCode
diff --git a/test/Elm/TestHelpers.hs b/test/Elm/TestHelpers.hs
new file mode 100644
--- /dev/null
+++ b/test/Elm/TestHelpers.hs
@@ -0,0 +1,9 @@
+module Elm.TestHelpers where
+
+import Elm.Derive
+
+fieldDropOpts :: Int -> DeriveOpts
+fieldDropOpts i =
+    defaultOpts
+    { do_fieldModifier = drop i
+    }
diff --git a/test/Elm/TyRenderSpec.hs b/test/Elm/TyRenderSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Elm/TyRenderSpec.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE TemplateHaskell #-}
+module Elm.TyRenderSpec (spec) where
+
+import Elm.Derive
+import Elm.TyRep
+import Elm.TyRender
+
+import Elm.TestHelpers
+
+import Data.Proxy
+import Test.Hspec
+
+data Foo
+   = Foo
+   { f_name :: String
+   , f_blablub :: Int
+   } deriving (Show, Eq)
+
+data Bar a
+   = Bar
+   { b_name :: a
+   , b_blablub :: Int
+   , b_tuple :: (Int, String)
+   , b_list :: [Bool]
+   } deriving (Show, Eq)
+
+data SomeOpts a
+   = Okay Int
+   | NotOkay a
+
+$(deriveElmDef (fieldDropOpts 2) ''Foo)
+$(deriveElmDef (fieldDropOpts 2) ''Bar)
+$(deriveElmDef defaultOpts ''SomeOpts)
+
+fooCode :: String
+fooCode = "type alias Foo  = \n   { name: String\n   , blablub: Int\n   }\n"
+
+barCode :: String
+barCode = "type alias Bar a = \n   { name: a\n   , blablub: Int\n   , tuple: (Int, String)\n   , list: (List Bool)\n   }\n"
+
+someOptsCode :: String
+someOptsCode = "type SomeOpts a = \n    Okay Int\n    | NotOkay a\n"
+
+spec :: Spec
+spec =
+    describe "deriveElmRep" $
+    do let rFoo = compileElmDef (Proxy :: Proxy Foo)
+           rBar = compileElmDef (Proxy :: Proxy (Bar a))
+           rSomeOpts = compileElmDef (Proxy :: Proxy (SomeOpts a))
+       it "should produce the correct code" $
+          do renderElm rFoo `shouldBe` fooCode
+             renderElm rBar `shouldBe` barCode
+             renderElm rSomeOpts `shouldBe` someOptsCode
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
