diff --git a/Data/Piso.hs b/Data/Piso.hs
new file mode 100644
--- /dev/null
+++ b/Data/Piso.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE DeriveFunctor #-}
+
+module Data.Piso (
+
+  -- * Partial isomorphisms
+  Piso(..), forward, backward,
+  FromPiso(..),
+  (:-)(..)
+
+  ) where
+
+
+import Prelude hiding (id, (.))
+
+import Control.Monad ((>=>))
+import Control.Category (Category(..))
+
+
+-- | Bidirectional isomorphism that is partial in the backward direction.
+--
+-- This can be used to express constructor-deconstructor pairs. For example:
+--
+-- > nil :: Piso t ([a] :- t)
+-- > nil = Piso f g
+-- >   where
+-- >     f        t  = [] :- t
+-- >     g ([] :- t) = Just t
+-- >     g _         = Nothing
+-- >
+-- > cons :: Piso (a :- [a] :- t) ([a] :- t)
+-- > cons = Piso f g
+-- >   where
+-- >     f (x :- xs  :- t) = (x : xs) :- t
+-- >     g ((x : xs) :- t) = Just (x :- xs :- t)
+-- >     g _               = Nothing
+--
+-- Here ':-' can be read as \'cons\', forming a stack of values. For example,
+-- @nil@ pushes @[]@ onto the stack; or, in the backward direction, tries to
+-- remove @[]@ from the stack. Representing constructor-destructor pairs as
+-- stack manipulators allows them to be composed more easily.
+--
+-- Module @Data.Piso.Common@ contains @Piso@s for some common datatypes.
+--
+-- Modules @Data.Piso.Generic@ and @Data.Piso.TH@ offer generic ways of deriving @Piso@s for custom datatypes.
+
+data Piso a b = Piso (a -> b) (b -> Maybe a)
+
+instance Category Piso where
+  id                          = Piso id Just
+  ~(Piso f1 g1) . ~(Piso f2 g2) = Piso (f1 . f2) (g1 >=> g2)
+
+
+-- | Apply an isomorphism in forward direction.
+forward :: Piso a b -> a -> b
+forward (Piso f _) = f
+
+-- | Apply an isomorphism in backward direction.
+backward :: Piso a b -> b -> Maybe a
+backward (Piso _ g) = g
+
+
+-- | A type class that expresses that a category is able to embed 'Piso' values.
+class Category cat => FromPiso cat where
+  fromPiso :: Piso a b -> cat a b
+
+instance FromPiso Piso where
+  fromPiso = id
+
+
+-- | Heterogenous stack with a head and a tail. Or: an infix way to write @(,)@.
+data h :- t = h :- t
+  deriving (Eq, Show, Functor)
+infixr 5 :-
diff --git a/Data/Piso/Common.hs b/Data/Piso/Common.hs
new file mode 100644
--- /dev/null
+++ b/Data/Piso/Common.hs
@@ -0,0 +1,70 @@
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE NoMonoPatBinds #-}
+
+-- | Constructor-destructor isomorphisms for some common datatypes.
+module Data.Piso.Common (
+
+  -- * @()@
+  unit,
+  
+  -- * @(,)@
+  tup,
+  
+  -- * @(,,)@
+  tup3,
+
+  -- * @Maybe a@
+  nothing, just,
+  
+  -- * @[a]@
+  nil, cons,
+  
+  -- * @Either a b@
+  left, right,
+  
+  -- * @Bool@
+  false, true
+
+  ) where
+
+import Prelude hiding (id, (.), maybe, either)
+
+import Data.Piso
+import Data.Piso.TH
+
+
+unit :: Piso t (() :- t)
+unit = $(derivePisos ''())
+
+tup :: Piso (a :- b :- t) ((a, b) :- t)
+tup = $(derivePisos ''(,))
+
+tup3 :: Piso (a :- b :- c :- t) ((a, b, c) :- t)
+tup3 = $(derivePisos ''(,,))
+
+nothing :: Piso t (Maybe a :- t)
+just    :: Piso (a :- t) (Maybe a :- t)
+(nothing, just) = $(derivePisos ''Maybe)
+
+nil :: Piso t ([a] :- t)
+nil = Piso f g
+  where
+    f        t  = [] :- t
+    g ([] :- t) = Just t
+    g _         = Nothing
+
+cons :: Piso (a :- [a] :- t) ([a] :- t)
+cons = Piso f g
+  where
+    f (x :- xs  :- t) = (x : xs) :- t
+    g ((x : xs) :- t) = Just (x :- xs :- t)
+    g _               = Nothing
+
+left  :: Piso (a :- t) (Either a b :- t)
+right :: Piso (b :- t) (Either a b :- t)
+(left, right) = $(derivePisos ''Either)
+
+false :: Piso t (Bool :- t)
+true  :: Piso t (Bool :- t)
+(false, true) = $(derivePisos ''Bool)
diff --git a/Data/Piso/Generic.hs b/Data/Piso/Generic.hs
new file mode 100644
--- /dev/null
+++ b/Data/Piso/Generic.hs
@@ -0,0 +1,119 @@
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE RankNTypes #-}
+
+module Data.Piso.Generic (mkPisoList, PisoList(..), PisoLhs) where
+
+import Data.Piso
+import GHC.Generics
+
+
+-- | Derive a list of partial isomorphisms, one for each constructor in the 'Generic' datatype @a@. The list is wrapped in the unary constructor @PisoList@. Within that constructor, the isomorphisms are separated by the right-associative binary infix constructor @:&@. Finally, the individual isomorphisms are wrapped in the unary constructor @I@. These constructors are all exported by this module, but no documentation is generated for them by Hackage.
+--
+-- As an example, here is how to define the isomorphisms @nil@ and @cons@ for @[a]@, which is an instance of @Generic@:
+--
+-- > nil  :: Piso              t  ([a] :- t)
+-- > cons :: Piso (a :- [a] :- t) ([a] :- t)
+-- > (nil, cons) = (nil', cons')
+-- >   where
+-- >     PisoList (I nil' :& I cons') = mkPisoList
+--
+-- GHC 7.6.3 requires the extra indirection through @nil'@ and @cons'@, due to bug 7268 (<http://ghc.haskell.org/trac/ghc/ticket/7268>). When it is fixed, the example above can be written in a more direct way:
+--
+-- > nil  :: Piso              t  ([a] :- t)
+-- > cons :: Piso (a :- [a] :- t) ([a] :- t)
+-- > PisoList (I nil :& I cons) = mkPisoList
+--
+-- If you are familiar with the generic representations from @Data.Generic@, you might be interested in the exact types of the various constructors in which the isomorphisms are wrapped:
+--
+-- > I        :: (forall t. Piso (PisoLhs f t) (a :- t)) -> PisoList (M1 C c f) a
+-- > (:&)     :: PisoList f a -> PisoList g a -> PisoList (f :+: g) a
+-- > PisoList :: PisoList f a -> PisoList (M1 D c f) a
+--
+-- The type constructor @PisoLhs@ that appears in the type of @I@ is an internal type family that builds the proper heterogenous list of types (using ':-') based on the constructor's fields.
+mkPisoList :: (Generic a, MkPisoList (Rep a)) => PisoList (Rep a) a
+mkPisoList = mkPisoList' to (Just . from)
+
+
+class MkPisoList (f :: * -> *) where
+  data PisoList (f :: * -> *) (a :: *)
+  mkPisoList' :: (f p -> a) -> (a -> Maybe (f q)) -> PisoList f a
+
+
+instance MkPisoList f => MkPisoList (M1 D c f) where
+  data PisoList (M1 D c f) a = PisoList (PisoList f a)
+  mkPisoList' f' g' = PisoList (mkPisoList' (f' . M1) (fmap unM1 . g'))
+
+
+infixr :&
+
+instance (MkPisoList f, MkPisoList g) => MkPisoList (f :+: g) where
+  data PisoList (f :+: g) a = PisoList f a :& PisoList g a
+  mkPisoList' f' g' = f f' g' :& g f' g'
+    where
+      f :: forall a p q. ((f :+: g) p -> a) -> (a -> Maybe ((f :+: g) q)) -> PisoList f a
+      f _f' _g' = mkPisoList' (\fp -> _f' (L1 fp)) (matchL _g')
+      g :: forall a p q. ((f :+: g) p -> a) -> (a -> Maybe ((f :+: g) q)) -> PisoList g a
+      g _f' _g' = mkPisoList' (\gp -> _f' (R1 gp)) (matchR _g')
+
+      matchL :: (a -> Maybe ((f :+: g) q)) -> a -> Maybe (f q)
+      matchL _g' a = case _g' a of
+        Just (L1 f'') -> Just f''
+        _ -> Nothing
+
+      matchR :: (a -> Maybe ((f :+: g) q)) -> a -> Maybe (g q)
+      matchR _g' a = case _g' a of
+        Just (R1 g'') -> Just g''
+        _ -> Nothing
+
+
+instance MkPiso f => MkPisoList (M1 C c f) where
+
+  data PisoList (M1 C c f) a = I (forall t cat. FromPiso cat => cat (PisoLhs f t) (a :- t))
+
+  mkPisoList' f' g' = I (fromPiso (Piso (f f') (g g')))
+    where
+      f :: forall a p t. (M1 C c f p -> a) -> PisoLhs f t -> a :- t
+      f _f' lhs = mapHead (_f' . M1) (mkR lhs)
+      g :: forall a p t. (a -> Maybe (M1 C c f p)) -> (a :- t) -> Maybe (PisoLhs f t)
+      g _g' (a :- t) = fmap (mkL . (:- t) . unM1) (_g' a)
+
+
+-- Deriving types and conversions for single constructors
+
+class MkPiso (f :: * -> *) where
+  type PisoLhs (f :: * -> *) (t :: *) :: *
+  mkR :: forall p t. PisoLhs f t -> (f p :- t)
+  mkL :: forall p t. (f p :- t) -> PisoLhs f t
+
+instance MkPiso U1 where
+  type PisoLhs U1 t = t
+  mkR t         = U1 :- t
+  mkL (U1 :- t) = t
+
+instance MkPiso (K1 i a) where
+  type PisoLhs (K1 i a) t = a :- t
+  mkR (h :- t) = K1 h :- t
+  mkL (K1 h :- t) = h :- t
+
+instance MkPiso f => MkPiso (M1 i c f) where
+  type PisoLhs (M1 i c f) t = PisoLhs f t
+  mkR = mapHead M1 . mkR
+  mkL = mkL . mapHead unM1
+
+instance (MkPiso f, MkPiso g) => MkPiso (f :*: g) where
+  type PisoLhs (f :*: g) t = PisoLhs f (PisoLhs g t)
+  mkR t = (hf :*: hg) :- tg
+    where
+      hf :- tf = mkR t
+      hg :- tg = mkR tf
+  mkL ((hf :*: hg) :- t) = mkL (hf :- mkL (hg :- t))
+
+
+
+mapHead :: (a -> b) -> (a :- t) -> (b :- t)
+mapHead f (h :- t) = f h :- t
diff --git a/Data/Piso/TH.hs b/Data/Piso/TH.hs
new file mode 100644
--- /dev/null
+++ b/Data/Piso/TH.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Data.Piso.TH (derivePisos) where
+
+import Data.Piso
+import Language.Haskell.TH
+import Control.Applicative
+import Control.Monad
+
+
+-- | Derive partial isomorphisms for a given datatype. The resulting
+-- expression is a tuple with one isomorphism element for each constructor in
+-- the datatype.
+-- 
+-- For example:
+-- 
+-- > nothing :: Piso t (Maybe a :- t)
+-- > just    :: Piso (a :- t) (Maybe a :- t)
+-- > (nothing, just) = $(derivePisos ''Maybe)
+-- 
+-- Deriving isomorphisms this way requires @-XNoMonoPatBinds@.
+derivePisos :: Name -> Q Exp
+derivePisos name = do
+  info <- reify name
+  routers <-
+    case info of
+      TyConI (DataD _ _ _ cons _)   ->
+        mapM (derivePiso (length cons /= 1)) cons
+      TyConI (NewtypeD _ _ _ con _) ->
+        (:[]) <$> derivePiso False con
+      _ ->
+        fail $ show name ++ " is not a datatype."
+  return (TupE routers)
+
+
+derivePiso :: Bool -> Con -> Q Exp
+derivePiso matchWildcard con =
+  case con of
+    NormalC name tys -> go name (map snd tys)
+    RecC name tys -> go name (map (\(_,_,ty) -> ty) tys)
+    _ -> fail $ "Unsupported constructor " ++ show (conName con)
+  where
+    go name tys = do
+      piso <- [| Piso |]
+      pisoCon <- deriveConstructor name tys
+      pisoDes <- deriveDestructor matchWildcard name tys
+      return $ piso `AppE` pisoCon `AppE` pisoDes
+
+
+deriveConstructor :: Name -> [Type] -> Q Exp
+deriveConstructor name tys = do
+  -- Introduce some names
+  t          <- newName "t"
+  fieldNames <- replicateM (length tys) (newName "a")
+
+  -- Figure out the names of some constructors
+  ConE cons  <- [| (:-) |]
+
+  let pat = foldr (\f fs -> ConP cons [VarP f, fs]) (VarP t) fieldNames
+  let applyCon = foldl (\f x -> f `AppE` VarE x) (ConE name) fieldNames
+  let body = ConE cons `AppE` applyCon `AppE` VarE t
+
+  return $ LamE [pat] body
+
+
+deriveDestructor :: Bool -> Name -> [Type] -> Q Exp
+deriveDestructor matchWildcard name tys = do
+  -- Introduce some names
+  x          <- newName "x"
+  r          <- newName "r"
+  fieldNames <- replicateM (length tys) (newName "a")
+
+  -- Figure out the names of some constructors
+  ConE just  <- [| Just |]
+  ConE cons  <- [| (:-) |]
+  nothing    <- [| Nothing |]
+
+  let conPat   = ConP name (map VarP fieldNames)
+  let okBody   = ConE just `AppE`
+                  foldr
+                    (\h t -> ConE cons `AppE` VarE h `AppE` t)
+                    (VarE r)
+                    fieldNames
+  let okCase   = Match (ConP cons [conPat, VarP r]) (NormalB okBody) []
+  let failCase = Match WildP (NormalB nothing) []
+  let allCases =
+        if matchWildcard
+              then [okCase, failCase]
+              else [okCase]
+
+  return $ LamE [VarP x] (CaseE (VarE x) allCases)
+
+
+-- Retrieve the name of a constructor.
+conName :: Con -> Name
+conName con =
+  case con of
+    NormalC name _  -> name
+    RecC name _     -> name
+    InfixC _ name _ -> name
+    ForallC _ _ con' -> conName con'
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2013, Martijn van Steenbergen
+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 the author nor the
+      names of his contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 <copyright holder> 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/Piso.cabal b/Piso.cabal
new file mode 100644
--- /dev/null
+++ b/Piso.cabal
@@ -0,0 +1,32 @@
+Name:           Piso
+Version:        0.1
+Synopsis:       Partial isomorphisms
+Description:   	Partial isomorphisms
+
+
+Author:         Martijn van Steenbergen
+Maintainer:     martijn@van.steenbergen.nl
+Stability:      Experimental
+Copyright:      Some Rights Reserved (CC) 2013 Martijn van Steenbergen
+Homepage:       https://github.com/MedeaMelana/Piso
+Bug-reports:    https://github.com/MedeaMelana/Piso/issues
+
+
+Cabal-Version:  >= 1.6
+License:        BSD3
+License-file:   LICENSE
+Category:       Data
+Build-type:     Simple
+
+
+Library
+  Exposed-Modules:  Data.Piso,
+                    Data.Piso.Common,
+                    Data.Piso.TH,
+                    Data.Piso.Generic
+  Build-Depends:    base >= 3.0 && < 5,
+                    template-haskell
+
+Source-Repository head
+  Type:         git
+  Location:     https://github.com/MedeaMelana/Piso
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
