diff --git a/siphon.cabal b/siphon.cabal
--- a/siphon.cabal
+++ b/siphon.cabal
@@ -1,6 +1,6 @@
 name:                siphon
-version:             0.2
-synopsis:            Generic types and functions for columnar encoding and decoding
+version:             0.6
+synopsis:            Encode and decode CSV files
 description:         Please see README.md
 homepage:            https://github.com/andrewthad/colonnade#readme
 license:             BSD3
@@ -26,7 +26,7 @@
     Siphon.Internal.Text
   build-depends:
       base >= 4.7 && < 5
-    , colonnade
+    , colonnade >= 1.1 && < 1.2
     , text
     , bytestring
     , contravariant
@@ -53,6 +53,7 @@
     , pipes
     , HUnit
     , test-framework-hunit
+    , profunctors
   ghc-options:         -threaded -rtsopts -with-rtsopts=-N
   default-language:    Haskell2010
 
diff --git a/src/Siphon/Decoding.hs b/src/Siphon/Decoding.hs
--- a/src/Siphon/Decoding.hs
+++ b/src/Siphon/Decoding.hs
@@ -1,24 +1,38 @@
-{-# LANGUAGE RankNTypes   #-}
+{-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE DeriveFunctor #-}
 
-module Siphon.Decoding where
+module Siphon.Decoding 
+  ( mkParseError
+  , headlessPipe
+  , indexedPipe
+  , headedPipe
+  , consumeGeneral
+  , pipeGeneral
+  , convertDecodeError
+  , headed
+  , headless
+  , indexed
+  ) where
 
 import Siphon.Types
-import Colonnade.Types
+import Colonnade (Headed(..),Headless(..))
 import Siphon.Internal (row,comma)
 import Data.Text (Text)
 import Data.ByteString (ByteString)
 import Pipes (yield,Pipe,Consumer',Producer,await)
 import Data.Vector (Vector)
+import Data.Functor.Contravariant (Contravariant(..))
+import Data.Char (chr)
 import qualified Data.Vector as Vector
-import qualified Colonnade.Decoding as Decoding
 import qualified Data.Attoparsec.ByteString as AttoByteString
 import qualified Data.ByteString.Char8 as ByteString
 import qualified Data.Attoparsec.Types as Atto
 
-mkParseError :: Int -> [String] -> String -> DecodingRowError f content
+mkParseError :: Int -> [String] -> String -> DecolonnadeRowError f content
 mkParseError i ctxs msg = id
-  $ DecodingRowError i
+  $ DecolonnadeRowError i
   $ RowErrorParse $ concat
     [ "Contexts: ["
     , concat ctxs
@@ -28,37 +42,37 @@
     ]
 
 -- | This is a convenience function for working with @pipes-text@.
---   It will convert a UTF-8 decoding error into a `DecodingRowError`,
+--   It will convert a UTF-8 decoding error into a `DecolonnadeRowError`,
 --   so the pipes can be properly chained together.
-convertDecodeError :: String -> Either (Producer ByteString m ()) () -> Maybe (DecodingRowError f c)
-convertDecodeError encodingName (Left _) = Just (DecodingRowError 0 (RowErrorMalformed encodingName))
+convertDecodeError :: String -> Either (Producer ByteString m ()) () -> Maybe (DecolonnadeRowError f c)
+convertDecodeError encodingName (Left _) = Just (DecolonnadeRowError 0 (RowErrorMalformed encodingName))
 convertDecodeError _ (Right ()) = Nothing
 
 -- | This is seldom useful but is included for completeness.
 headlessPipe :: Monad m
   => Siphon c
-  -> Decoding Headless c a
-  -> Pipe c a m (DecodingRowError Headless c)
+  -> Decolonnade Headless c a
+  -> Pipe c a m (DecolonnadeRowError Headless c)
 headlessPipe sd decoding = uncheckedPipe requiredLength 0 sd indexedDecoding Nothing
   where
-  indexedDecoding = Decoding.headlessToIndexed decoding
-  requiredLength = Decoding.length indexedDecoding
+  indexedDecoding = headlessToIndexed decoding
+  requiredLength = decLength indexedDecoding
 
 indexedPipe :: Monad m
   => Siphon c
-  -> Decoding (Indexed Headless) c a
-  -> Pipe c a m (DecodingRowError Headless c)
+  -> Decolonnade (Indexed Headless) c a
+  -> Pipe c a m (DecolonnadeRowError Headless c)
 indexedPipe sd decoding = do
   e <- consumeGeneral 0 sd mkParseError
   case e of
     Left err -> return err
     Right (firstRow, mleftovers) ->
-      let req = Decoding.maxIndex decoding
+      let req = maxIndex decoding
           vlen = Vector.length firstRow
        in if vlen < req
-            then return (DecodingRowError 0 (RowErrorMinSize req vlen))
-            else case Decoding.uncheckedRun decoding firstRow of
-              Left cellErr -> return $ DecodingRowError 0 $ RowErrorDecode cellErr
+            then return (DecolonnadeRowError 0 (RowErrorMinSize req vlen))
+            else case uncheckedRun decoding firstRow of
+              Left cellErr -> return $ DecolonnadeRowError 0 $ RowErrorDecode cellErr
               Right a -> do
                 yield a
                 uncheckedPipe vlen 1 sd decoding mleftovers
@@ -66,15 +80,15 @@
 
 headedPipe :: (Monad m, Eq c)
   => Siphon c
-  -> Decoding Headed c a
-  -> Pipe c a m (DecodingRowError Headed c)
+  -> Decolonnade Headed c a
+  -> Pipe c a m (DecolonnadeRowError Headed c)
 headedPipe sd decoding = do
   e <- consumeGeneral 0 sd mkParseError
   case e of
     Left err -> return err
     Right (headers, mleftovers) ->
-      case Decoding.headedToIndexed headers decoding of
-        Left headingErrs -> return (DecodingRowError 0 (RowErrorHeading headingErrs))
+      case headedToIndexed headers decoding of
+        Left headingErrs -> return (DecolonnadeRowError 0 (RowErrorHeading headingErrs))
         Right indexedDecoding ->
           let requiredLength = Vector.length headers
            in uncheckedPipe requiredLength 1 sd indexedDecoding mleftovers
@@ -84,18 +98,18 @@
   => Int -- ^ expected length of each row
   -> Int -- ^ index of first row, usually zero or one
   -> Siphon c
-  -> Decoding (Indexed f) c a
+  -> Decolonnade (Indexed f) c a
   -> Maybe c
-  -> Pipe c a m (DecodingRowError f c)
+  -> Pipe c a m (DecolonnadeRowError f c)
 uncheckedPipe requiredLength ix sd d mleftovers =
   pipeGeneral ix sd mkParseError checkedRunWithRow mleftovers
   where
   checkedRunWithRow rowIx v =
     let vlen = Vector.length v in
     if vlen /= requiredLength
-      then Left $ DecodingRowError rowIx
+      then Left $ DecolonnadeRowError rowIx
                 $ RowErrorSize requiredLength vlen
-      else Decoding.uncheckedRunWithRow rowIx d v
+      else uncheckedRunWithRow rowIx d v
 
 consumeGeneral :: Monad m
   => Int
@@ -156,5 +170,170 @@
   go = do
     a <- await
     if f a then go else return a
+
+-- | Converts the content type of a 'Decolonnade'. The @'Contravariant' f@
+-- constraint means that @f@ can be 'Headless' but not 'Headed'.
+contramapContent :: forall c1 c2 f a. Contravariant f => (c2 -> c1) -> Decolonnade f c1 a -> Decolonnade f c2 a
+contramapContent f = go
+  where
+  go :: forall b. Decolonnade f c1 b -> Decolonnade f c2 b
+  go (DecolonnadePure x) = DecolonnadePure x
+  go (DecolonnadeAp h decode apNext) =
+    DecolonnadeAp (contramap f h) (decode . f) (go apNext)
+
+headless :: (content -> Either String a) -> Decolonnade Headless content a
+headless f = DecolonnadeAp Headless f (DecolonnadePure id)
+
+headed :: content -> (content -> Either String a) -> Decolonnade Headed content a
+headed h f = DecolonnadeAp (Headed h) f (DecolonnadePure id)
+
+indexed :: Int -> (content -> Either String a) -> Decolonnade (Indexed Headless) content a
+indexed ix f = DecolonnadeAp (Indexed ix Headless) f (DecolonnadePure id)
+
+maxIndex :: forall f c a. Decolonnade (Indexed f) c a -> Int
+maxIndex = go 0 where
+  go :: forall b. Int -> Decolonnade (Indexed f) c b -> Int
+  go !ix (DecolonnadePure _) = ix
+  go !ix1 (DecolonnadeAp (Indexed ix2 _) decode apNext) =
+    go (max ix1 ix2) apNext
+
+-- | This function uses 'unsafeIndex' to access
+--   elements of the 'Vector'.
+uncheckedRunWithRow ::
+     Int
+  -> Decolonnade (Indexed f) content a
+  -> Vector content
+  -> Either (DecolonnadeRowError f content) a
+uncheckedRunWithRow i d v = mapLeft (DecolonnadeRowError i . RowErrorDecode) (uncheckedRun d v)
+
+-- | This function does not check to make sure that the indicies in
+--   the 'Decolonnade' are in the 'Vector'.
+uncheckedRun :: forall content a f.
+                Decolonnade (Indexed f) content a
+             -> Vector content
+             -> Either (DecolonnadeCellErrors f content) a
+uncheckedRun dc v = getEitherWrap (go dc)
+  where
+  go :: forall b.
+        Decolonnade (Indexed f) content b
+     -> EitherWrap (DecolonnadeCellErrors f content) b
+  go (DecolonnadePure b) = EitherWrap (Right b)
+  go (DecolonnadeAp ixed@(Indexed ix h) decode apNext) =
+    let rnext = go apNext
+        content = Vector.unsafeIndex v ix
+        rcurrent = mapLeft (DecolonnadeCellErrors . Vector.singleton . DecolonnadeCellError content ixed) (decode content)
+    in rnext <*> (EitherWrap rcurrent)
+
+headlessToIndexed :: forall c a.
+  Decolonnade Headless c a -> Decolonnade (Indexed Headless) c a
+headlessToIndexed = go 0 where
+  go :: forall b. Int -> Decolonnade Headless c b -> Decolonnade (Indexed Headless) c b
+  go !ix (DecolonnadePure a) = DecolonnadePure a
+  go !ix (DecolonnadeAp Headless decode apNext) =
+    DecolonnadeAp (Indexed ix Headless) decode (go (ix + 1) apNext)
+
+decLength :: forall f c a. Decolonnade f c a -> Int
+decLength = go 0 where
+  go :: forall b. Int -> Decolonnade f c b -> Int
+  go !a (DecolonnadePure _) = a
+  go !a (DecolonnadeAp _ _ apNext) = go (a + 1) apNext
+
+-- | Maps over a 'Decolonnade' that expects headers, converting these
+--   expected headers into the indices of the columns that they
+--   correspond to.
+headedToIndexed :: forall content a. Eq content
+                => Vector content -- ^ Headers in the source document
+                -> Decolonnade Headed content a -- ^ Decolonnade that contains expected headers
+                -> Either (HeadingErrors content) (Decolonnade (Indexed Headed) content a)
+headedToIndexed v = getEitherWrap . go
+  where
+  go :: forall b. Eq content
+     => Decolonnade Headed content b
+     -> EitherWrap (HeadingErrors content) (Decolonnade (Indexed Headed) content b)
+  go (DecolonnadePure b) = EitherWrap (Right (DecolonnadePure b))
+  go (DecolonnadeAp hd@(Headed h) decode apNext) =
+    let rnext = go apNext
+        ixs = Vector.elemIndices h v
+        ixsLen = Vector.length ixs
+        rcurrent
+          | ixsLen == 1 = Right (Vector.unsafeIndex ixs 0)
+          | ixsLen == 0 = Left (HeadingErrors (Vector.singleton h) Vector.empty)
+          | otherwise   = Left (HeadingErrors Vector.empty (Vector.singleton (h,ixsLen)))
+    in (\ix ap -> DecolonnadeAp (Indexed ix hd) decode ap)
+       <$> EitherWrap rcurrent
+       <*> rnext
+
+-- | This adds one to the index because text editors consider
+--   line number to be one-based, not zero-based.
+prettyError :: (c -> String) -> DecolonnadeRowError f c -> String
+prettyError toStr (DecolonnadeRowError ix e) = unlines
+  $ ("Decolonnade error on line " ++ show (ix + 1) ++ " of file.")
+  : ("Error Category: " ++ descr)
+  : map ("  " ++) errDescrs
+  where (descr,errDescrs) = prettyRowError toStr e
+
+prettyRowError :: (content -> String) -> RowError f content -> (String, [String])
+prettyRowError toStr x = case x of
+  RowErrorParse err -> (,) "CSV Parsing"
+    [ "The line could not be parsed into cells correctly."
+    , "Original parser error: " ++ err
+    ]
+  RowErrorSize reqLen actualLen -> (,) "Row Length"
+    [ "Expected the row to have exactly " ++ show reqLen ++ " cells."
+    , "The row only has " ++ show actualLen ++ " cells."
+    ]
+  RowErrorMinSize reqLen actualLen -> (,) "Row Min Length"
+    [ "Expected the row to have at least " ++ show reqLen ++ " cells."
+    , "The row only has " ++ show actualLen ++ " cells."
+    ]
+  RowErrorMalformed enc -> (,) "Text Decolonnade"
+    [ "Tried to decode the input as " ++ enc ++ " text"
+    , "There is a mistake in the encoding of the text."
+    ]
+  RowErrorHeading errs -> (,) "Header" (prettyHeadingErrors toStr errs)
+  RowErrorDecode errs -> (,) "Cell Decolonnade" (prettyCellErrors toStr errs)
+
+prettyCellErrors :: (c -> String) -> DecolonnadeCellErrors f c -> [String]
+prettyCellErrors toStr (DecolonnadeCellErrors errs) = drop 1 $
+  flip concatMap errs $ \(DecolonnadeCellError content (Indexed ix _) msg) ->
+    let str = toStr content in
+    [ "-----------"
+    , "Column " ++ columnNumToLetters ix
+    , "Original parse error: " ++ msg
+    , "Cell Content Length: " ++ show (Prelude.length str)
+    , "Cell Content: " ++ if null str
+        then "[empty cell]"
+        else str
+    ]
+
+prettyHeadingErrors :: (c -> String) -> HeadingErrors c -> [String]
+prettyHeadingErrors conv (HeadingErrors missing duplicates) = concat
+  [ concatMap (\h -> ["The header " ++ conv h ++ " was missing."]) missing
+  , concatMap (\(h,n) -> ["The header " ++ conv h ++ " occurred " ++ show n ++ " times."]) duplicates
+  ]
+
+columnNumToLetters :: Int -> String
+columnNumToLetters i
+  | i >= 0 && i < 25 = [chr (i + 65)]
+  | otherwise = "Beyond Z. Fix this."
+
+
+newtype EitherWrap a b = EitherWrap
+  { getEitherWrap :: Either a b
+  } deriving (Functor)
+
+instance Monoid a => Applicative (EitherWrap a) where
+  pure = EitherWrap . Right
+  EitherWrap (Left a1) <*> EitherWrap (Left a2) = EitherWrap (Left (mappend a1 a2))
+  EitherWrap (Left a1) <*> EitherWrap (Right _) = EitherWrap (Left a1)
+  EitherWrap (Right _) <*> EitherWrap (Left a2) = EitherWrap (Left a2)
+  EitherWrap (Right f) <*> EitherWrap (Right b) = EitherWrap (Right (f b))
+
+mapLeft :: (a -> b) -> Either a c -> Either b c
+mapLeft _ (Right a) = Right a
+mapLeft f (Left a) = Left (f a)
+
+
+
 
 
diff --git a/src/Siphon/Encoding.hs b/src/Siphon/Encoding.hs
--- a/src/Siphon/Encoding.hs
+++ b/src/Siphon/Encoding.hs
@@ -1,33 +1,28 @@
 module Siphon.Encoding where
 
 import Siphon.Types
-import Colonnade.Types
+import Colonnade (Colonnade,Headed)
 import Pipes (Pipe,yield)
 import qualified Pipes.Prelude as Pipes
-import qualified Colonnade.Encoding as Encoding
+import qualified Colonnade.Encode as E
 
-row :: Siphon c
-    -> Encoding f c a
-    -> a
-    -> c
+row :: Siphon c -> Colonnade f a c -> a -> c
 row (Siphon escape intercalate _ _) e =
-  intercalate . Encoding.runRow escape e
+  intercalate . E.row escape e
 
-header :: Siphon c
-       -> Encoding Headed c a
-       -> c
+header :: Siphon c -> Colonnade Headed a c -> c
 header (Siphon escape intercalate _ _) e =
-  intercalate (Encoding.runHeader escape e)
+  intercalate (E.header escape e)
 
 pipe :: Monad m
   => Siphon c
-  -> Encoding f c a
+  -> Colonnade f a c
   -> Pipe a c m x
 pipe siphon encoding = Pipes.map (row siphon encoding)
 
 headedPipe :: Monad m
   => Siphon c
-  -> Encoding Headed c a
+  -> Colonnade Headed a c
   -> Pipe a c m x
 headedPipe siphon encoding = do
   yield (header siphon encoding)
diff --git a/src/Siphon/Types.hs b/src/Siphon/Types.hs
--- a/src/Siphon/Types.hs
+++ b/src/Siphon/Types.hs
@@ -1,7 +1,13 @@
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
 module Siphon.Types where
 
 import Data.Vector (Vector)
-import Colonnade.Types (DecodingRowError)
+import Control.Exception (Exception)
+import Data.Typeable (Typeable)
+import qualified Data.Vector as Vector
 import qualified Data.Attoparsec.Types as Atto
 
 newtype Escaped c = Escaped { getEscaped :: c }
@@ -12,6 +18,77 @@
   , siphonParseRow    :: c -> Atto.IResult c (Vector c)
   , siphonNull        :: c -> Bool
   }
+
+data DecolonnadeCellError f content = DecolonnadeCellError
+  { decodingCellErrorContent :: !content
+  , decodingCellErrorHeader  :: !(Indexed f content)
+  , decodingCellErrorMessage :: !String
+  } deriving (Show,Read,Eq)
+
+-- instance (Show (f content), Typeable content) => Exception (DecolonnadeError f content)
+
+data Indexed f a = Indexed
+  { indexedIndex :: !Int
+  , indexedHeading :: !(f a)
+  } deriving (Eq,Ord,Functor,Show,Read)
+
+newtype DecolonnadeCellErrors f content = DecolonnadeCellErrors
+  { getDecolonnadeCellErrors :: Vector (DecolonnadeCellError f content)
+  } deriving (Monoid,Show,Read,Eq)
+
+-- newtype ParseRowError = ParseRowError String
+
+-- TODO: rewrite the instances for this by hand. They
+-- currently use FlexibleContexts.
+data DecolonnadeRowError f content = DecolonnadeRowError
+  { decodingRowErrorRow   :: !Int
+  , decodingRowErrorError :: !(RowError f content)
+  } deriving (Show,Read,Eq)
+
+-- TODO: rewrite the instances for this by hand. They
+-- currently use FlexibleContexts.
+data RowError f content
+  = RowErrorParse !String -- ^ Error occurred parsing the document into cells
+  | RowErrorDecode !(DecolonnadeCellErrors f content) -- ^ Error decoding the content
+  | RowErrorSize !Int !Int -- ^ Wrong number of cells in the row
+  | RowErrorHeading !(HeadingErrors content)
+  | RowErrorMinSize !Int !Int
+  | RowErrorMalformed !String -- ^ Error decoding unicode content
+  deriving (Show,Read,Eq)
+
+data HeadingErrors content = HeadingErrors
+  { headingErrorsMissing   :: Vector content       -- ^ headers that were missing
+  , headingErrorsDuplicate :: Vector (content,Int) -- ^ headers that occurred more than once
+  } deriving (Show,Read,Eq)
+
+instance (Show content, Typeable content) => Exception (HeadingErrors content)
+
+instance Monoid (HeadingErrors content) where
+  mempty = HeadingErrors Vector.empty Vector.empty
+  mappend (HeadingErrors a1 b1) (HeadingErrors a2 b2) = HeadingErrors
+    (a1 Vector.++ a2) (b1 Vector.++ b2)
+
+-- | This just actually a specialization of the free applicative.
+--   Check out @Control.Applicative.Free@ in the @free@ library to
+--   learn more about this. The meanings of the fields are documented
+--   slightly more in the source code. Unfortunately, haddock does not
+--   play nicely with GADTs.
+data Decolonnade f content a where
+  DecolonnadePure :: !a -- function
+               -> Decolonnade f content a
+  DecolonnadeAp :: !(f content) -- header
+             -> !(content -> Either String a) -- decoding function
+             -> !(Decolonnade f content (a -> b)) -- next decoding
+             -> Decolonnade f content b
+
+instance Functor (Decolonnade f content) where
+  fmap f (DecolonnadePure a) = DecolonnadePure (f a)
+  fmap f (DecolonnadeAp h c apNext) = DecolonnadeAp h c ((f .) <$> apNext)
+
+instance Applicative (Decolonnade f content) where
+  pure = DecolonnadePure
+  DecolonnadePure f <*> y = fmap f y
+  DecolonnadeAp h c y <*> z = DecolonnadeAp h c (flip <$> y <*> z)
 
 -- -- | This type is provided for convenience with @pipes-text@
 -- data CsvResult f c
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -12,26 +12,25 @@
 import Data.Text                            (Text)
 import GHC.Generics                         (Generic)
 import Data.Either.Combinators
-import Colonnade.Types
 import Siphon.Types
 import Data.Functor.Identity
 import Data.Functor.Contravariant           (contramap)
 import Data.Functor.Contravariant.Divisible (divided,conquered)
+import Colonnade (headed,headless,Colonnade,Headed,Headless)
+import Data.Profunctor (lmap)
 import qualified Data.Text                  as Text
 import qualified Data.ByteString.Builder    as Builder
 import qualified Data.ByteString.Lazy       as LByteString
 import qualified Data.ByteString            as ByteString
 import qualified Data.ByteString.Char8      as BC8
-import qualified Colonnade.Decoding         as Decoding
-import qualified Colonnade.Encoding         as Encoding
-import qualified Colonnade.Decoding.ByteString.Char8 as CDB
-import qualified Colonnade.Encoding.ByteString.Char8 as CEB
-import qualified Colonnade.Decoding.Text    as CDT
-import qualified Colonnade.Encoding.Text    as CET
+import qualified Colonnade                  as Colonnade
 import qualified Siphon.Encoding            as SE
 import qualified Siphon.Decoding            as SD
 import qualified Siphon.Content             as SC
 import qualified Pipes.Prelude              as Pipes
+import qualified Data.Text.Lazy as LText
+import qualified Data.Text.Lazy.Builder as TBuilder
+import qualified Data.Text.Lazy.Builder.Int as TBuilder
 import Pipes
 
 main :: IO ()
@@ -122,56 +121,56 @@
 decodeFoo :: (c -> String) -> c -> Either String Foo
 decodeFoo f = fooFromString . f
 
-decodingA :: Decoding Headless ByteString (Int,Char,Bool)
+decodingA :: Decolonnade Headless ByteString (Int,Char,Bool)
 decodingA = (,,)
-  <$> Decoding.headless CDB.int
-  <*> Decoding.headless CDB.char
-  <*> Decoding.headless CDB.bool
+  <$> SD.headless dbInt
+  <*> SD.headless dbChar
+  <*> SD.headless dbBool
 
-decodingB :: Decoding Headed ByteString (Int,Char,Bool)
+decodingB :: Decolonnade Headed ByteString (Int,Char,Bool)
 decodingB = (,,)
-  <$> Decoding.headed "number" CDB.int
-  <*> Decoding.headed "letter" CDB.char
-  <*> Decoding.headed "boolean" CDB.bool
+  <$> SD.headed "number" dbInt
+  <*> SD.headed "letter" dbChar
+  <*> SD.headed "boolean" dbBool
 
-encodingA :: Encoding Headless ByteString (Int,Char,Bool)
-encodingA = contramap tripleToPairs
-  $ divided (Encoding.headless CEB.int)
-  $ divided (Encoding.headless CEB.char)
-  $ divided (Encoding.headless CEB.bool)
-  $ conquered
+encodingA :: Colonnade Headless (Int,Char,Bool) ByteString
+encodingA = mconcat
+  [ lmap fst3 (headless ebInt)
+  , lmap snd3 (headless ebChar)
+  , lmap thd3 (headless ebBool)
+  ]
 
-encodingW :: Encoding Headless Text (Int,Char,Bool)
-encodingW = contramap tripleToPairs
-  $ divided (Encoding.headless CET.int)
-  $ divided (Encoding.headless CET.char)
-  $ divided (Encoding.headless CET.bool)
-  $ conquered
+encodingW :: Colonnade Headless (Int,Char,Bool) Text
+encodingW = mconcat
+  [ lmap fst3 (headless etInt)
+  , lmap snd3 (headless etChar)
+  , lmap thd3 (headless etBool)
+  ]
 
-encodingY :: Encoding Headless Text (Foo,Foo,Foo)
-encodingY = contramap tripleToPairs
-  $ divided (Encoding.headless $ encodeFoo Text.pack)
-  $ divided (Encoding.headless $ encodeFoo Text.pack)
-  $ divided (Encoding.headless $ encodeFoo Text.pack)
-  $ conquered
+encodingY :: Colonnade Headless (Foo,Foo,Foo) Text
+encodingY = mconcat
+  [ lmap fst3 (headless $ encodeFoo Text.pack)
+  , lmap snd3 (headless $ encodeFoo Text.pack)
+  , lmap thd3 (headless $ encodeFoo Text.pack)
+  ]
 
-decodingY :: Decoding Headless Text (Foo,Foo,Foo)
+decodingY :: Decolonnade Headless Text (Foo,Foo,Foo)
 decodingY = (,,)
-  <$> Decoding.headless (decodeFoo Text.unpack)
-  <*> Decoding.headless (decodeFoo Text.unpack)
-  <*> Decoding.headless (decodeFoo Text.unpack)
+  <$> SD.headless (decodeFoo Text.unpack)
+  <*> SD.headless (decodeFoo Text.unpack)
+  <*> SD.headless (decodeFoo Text.unpack)
 
-encodingB :: Encoding Headed ByteString (Int,Char,Bool)
-encodingB = contramap tripleToPairs
-  $ divided (Encoding.headed "number" CEB.int)
-  $ divided (Encoding.headed "letter" CEB.char)
-  $ divided (Encoding.headed "boolean" CEB.bool)
-  $ conquered
+encodingB :: Colonnade Headed (Int,Char,Bool) ByteString
+encodingB = mconcat
+  [ lmap fst3 (headed "number" ebInt)
+  , lmap snd3 (headed "letter" ebChar)
+  , lmap thd3 (headed "boolean" ebBool)
+  ]
 
-encodingC :: Encoding Headed ByteString (Int,Char,Bool)
+encodingC :: Colonnade Headed (Int,Char,Bool) ByteString
 encodingC = mconcat
-  [ contramap thd3 $ Encoding.headed "boolean" CEB.bool
-  , contramap snd3 $ Encoding.headed "letter" CEB.char
+  [ lmap thd3 $ headed "boolean" ebBool
+  , lmap snd3 $ headed "letter" ebChar
   ]
 
 tripleToPairs :: (a,b,c) -> (a,(b,(c,())))
@@ -182,8 +181,8 @@
 
 runTestScenario :: (Monoid c, Eq c, Show c)
   => Siphon c
-  -> (Siphon c -> Encoding f c (Int,Char,Bool) -> Pipe (Int,Char,Bool) c Identity ())
-  -> Encoding f c (Int,Char,Bool)
+  -> (Siphon c -> Colonnade f (Int,Char,Bool) c -> Pipe (Int,Char,Bool) c Identity ())
+  -> Colonnade f (Int,Char,Bool) c
   -> c
   -> Assertion
 runTestScenario s p e c =
@@ -193,8 +192,8 @@
 
 runCustomTestScenario :: (Monoid c, Eq c, Show c)
   => Siphon c
-  -> (Siphon c -> Encoding f c a -> Pipe a c Identity ())
-  -> Encoding f c a
+  -> (Siphon c -> Colonnade f a c -> Pipe a c Identity ())
+  -> Colonnade f a c
   -> a
   -> c
   -> Assertion
@@ -224,4 +223,57 @@
 -- | Take the third item out of a 3 element tuple
 thd3 :: (a,b,c) -> c
 thd3 (a,b,c) = c
+
+
+dbChar :: ByteString -> Either String Char
+dbChar b = case BC8.length b of
+  1 -> Right (BC8.head b)
+  0 -> Left "cannot decode Char from empty bytestring"
+  _ -> Left "cannot decode Char from multi-character bytestring"
+
+dbInt :: ByteString -> Either String Int
+dbInt b = do
+  (a,bsRem) <- maybe (Left "could not parse int") Right (BC8.readInt b)
+  if ByteString.null bsRem
+    then Right a
+    else Left "found extra characters after int"
+
+dbBool :: ByteString -> Either String Bool
+dbBool b
+  | b == BC8.pack "true" = Right True
+  | b == BC8.pack "false" = Right False
+  | otherwise = Left "must be true or false"
+
+ebChar :: Char -> ByteString
+ebChar = BC8.singleton
+
+ebInt :: Int -> ByteString
+ebInt = LByteString.toStrict
+    . Builder.toLazyByteString 
+    . Builder.intDec
+
+ebBool :: Bool -> ByteString
+ebBool x = case x of
+  True -> BC8.pack "true"
+  False -> BC8.pack "false"
+
+ebByteString :: ByteString -> ByteString
+ebByteString = id
+
+
+etChar :: Char -> Text
+etChar = Text.singleton
+
+etInt :: Int -> Text
+etInt = LText.toStrict
+    . TBuilder.toLazyText
+    . TBuilder.decimal
+
+etText :: Text -> Text
+etText = id
+
+etBool :: Bool -> Text
+etBool x = case x of
+  True -> Text.pack "true"
+  False -> Text.pack "false"
 
