diff --git a/ChangeLog b/ChangeLog
new file mode 100644
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,26 @@
+# 0.6.1
+
+- Fixing documentation typos
+- Implementing basic context merging (see
+  <https://github.com/lymar/hastache/issues/31>)
+- `mkGenericContext'` takes an additional `(String -> String)`
+  argument which is applied to record field names, much like aeson's
+  `fieldLabelModifier`
+- Removing unnecessary `utf8-string` dependency
+- Implementing the generic context creation for `Maybe` (see
+  <https://github.com/lymar/hastache/issues/38> and
+  `nestedPolyGenericContextTest` test), `Either`, `()`, and `Version`.
+- `mkReadme` is now officially an executable
+- Big change: custom extensions for generic contexts (should solve
+  #30). See the documentation for `Text.Hastache.Context` and the
+  `genericCustom.hs` example.
+- Making Hastache work with new base-4.8 (see #41)
+
+Thanks to: Tobias Florek, Edsko de Vries, Janne Hellsten, @clinty,
+Stefan Kersten, Herbert Valerio Riedel, and others
+
+# 0.6.0
+
+- Switching from lazy ByteString to lazy Text
+- Support for multiple constructors in generic contexts (issue #16)
+- Additional `MuVar` instances (`Maybe`, `Either`, `()`)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -20,10 +20,10 @@
 ```haskell
 import Text.Hastache 
 import Text.Hastache.Context 
-import qualified Data.ByteString.Lazy.Char8 as LZ 
+import qualified Data.Text.Lazy.IO as TL
 
 main = hastacheStr defaultConfig (encodeStr template) (mkStrContext context)
-    >>= LZ.putStrLn
+    >>= TL.putStrLn
 
 template = "Hello, {{name}}!\n\nYou have {{unread}} unread messages." 
 
@@ -43,12 +43,12 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 import Text.Hastache 
 import Text.Hastache.Context 
-import qualified Data.ByteString.Lazy as LZ 
+import qualified Data.Text.Lazy.IO as TL 
 import Data.Data 
 import Data.Generics 
 
 main = hastacheStr defaultConfig (encodeStr template) context
-    >>= LZ.putStrLn
+    >>= TL.putStrLn
 
 data Info = Info { 
     name    :: String, 
@@ -114,7 +114,7 @@
 
 ```haskell
 main = mapM_ (\(template,context) ->
-    hastacheStr defaultConfig (encodeStr template) context >>= LZ.putStrLn) 
+    hastacheStr defaultConfig (encodeStr template) context >>= TL.putStrLn) 
         [(template1, mkStrContext context1),
          (template1, context2),
          (template3, context3)]
@@ -185,7 +185,7 @@
 ```haskell
 main = mapM_ (\ctx ->
     hastacheStr defaultConfig (encodeStr template) (mkStrContext ctx)
-    >>= LZ.putStrLn) [context1,context2]
+    >>= TL.putStrLn) [context1,context2]
 
 template = "{{#msg}}{{msg}}{{/msg}}{{^msg}}No{{/msg}} new messages."
 
@@ -197,6 +197,65 @@
 No new messages.
 ```
 
+Multiple constructors (in generic context)
+
+```haskell
+#!/usr/local/bin/runhaskell
+-- | Multiple constructors in generic contexts
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings  #-}
+import           Data.Data
+import           Data.Monoid
+import           Data.Typeable                 ()
+
+import qualified Data.Text.Lazy    as TL
+import qualified Data.Text.Lazy.IO as TL
+import           Text.Hastache
+import           Text.Hastache.Context
+
+data Hero = SuperHero { name      :: String
+                      , powers    :: [String]
+                      , companion :: String
+                      }
+          | EvilHero  { name   :: String
+                      , minion :: String
+                      }
+          deriving (Show, Data, Typeable)
+
+template :: String
+template = mconcat [
+    "{{#SuperHero}}\n",
+    "Hero: {{name}}\n",
+    " * Powers: {{#powers}}\n",
+    "\n   - {{.}}{{/powers}} \n",
+    " * Companion: {{companion}}\n",
+    "{{/SuperHero}}\n",
+    "{{#EvilHero}}\n",
+    "Evil hero: {{name}}\n",
+    " * Minion: {{minion}}\n",
+    "{{/EvilHero}}"]
+
+render :: Hero -> IO TL.Text
+render = hastacheStr defaultConfig (encodeStr template)
+       . mkGenericContext
+
+main :: IO ()
+main = do let batman = SuperHero "Batman" ["ht","ht"] "Robin"
+          let doctorEvil = EvilHero "Doctor Evil" "Mini-Me"
+          render batman >>= TL.putStrLn
+          render doctorEvil >>= TL.putStrLn
+```
+```
+Hero: Batman
+ * Powers: 
+   - ht
+   - ht 
+ * Companion: Robin
+
+Evil hero: Doctor Evil
+ * Minion: Mini-Me
+```
+
 #### Functions
 
 ```haskell
@@ -214,15 +273,16 @@
 ```haskell
 {-# LANGUAGE FlexibleContexts #-}
 import Text.Hastache 
-import Text.Hastache.Context 
-import qualified Data.ByteString.Lazy.Char8 as LZ 
+import Text.Hastache.Context
+import qualified Data.Text.Lazy as TL 
+import qualified Data.Text.Lazy.IO as TL 
 import Control.Monad.State 
 
-main = run >>= LZ.putStrLn
+main = run >>= TL.putStrLn
 
 run = evalStateT stateFunc ""
 
-stateFunc :: StateT String IO LZ.ByteString
+stateFunc :: StateT String IO TL.Text
 stateFunc = 
     hastacheStr defaultConfig (encodeStr template) (mkStrContext context) 
 
@@ -242,6 +302,70 @@
 aaa aaabbb aaabbbccc
 ```
 
+#### Custom queries and field renaming
+
+```haskell
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+
+-- Custom extension function for types that are not supported out of
+-- the box in generic contexts
+import Text.Hastache 
+import Text.Hastache.Context 
+
+import qualified Data.Text.Lazy as TL 
+import qualified Data.Text.Lazy.IO as TL 
+
+import Data.Data (Data, Typeable)
+import Data.Decimal
+import Data.Generics.Aliases (extQ)
+
+data DecimalOrInf = Inf | Dec Decimal deriving (Data, Typeable)
+deriving instance Data Decimal
+data Test = Test {n::Int, m::DecimalOrInf} deriving (Data, Typeable)
+
+
+val1 :: Test
+val1 = Test 1 (Dec $ Decimal 3 1500)
+
+val2 :: Test
+val2 = Test 2 Inf
+
+query :: Ext
+query = defaultExt `extQ` f
+  where f Inf = "+inf"
+        f (Dec i) = show i
+
+r "m" = "moo"
+r x   = x
+
+example :: Test -> IO TL.Text
+example v = hastacheStr defaultConfig
+                        (encodeStr template)
+                        (mkGenericContext' r query v)
+
+template = concat [ 
+     "An int: {{n}}\n",
+     "{{#moo.Dec}}A decimal number: {{moo.Dec}}{{/moo.Dec}}",
+     "{{#moo.Inf}}An infinity: {{moo.Inf}}{{/moo.Inf}}"
+     ] 
+
+main = do
+  example val1 >>= TL.putStrLn
+  example val2 >>= TL.putStrLn
+```
+
+```
+An int: 1
+A decimal number: 1.500
+An int: 2
+An infinity: +inf
+```
+
+
 #### Generics big example
 
 ```haskell
@@ -297,3 +421,4 @@
 
  * [Hastache test](https://github.com/lymar/hastache/blob/master/tests/test.hs)
  * Real world example: [README.md file generator](https://github.com/lymar/hastache/blob/master/mkReadme.hs)
+ * [examples/ directory](https://github.com/lymar/hastache/tree/master/examples)
diff --git a/Text/Hastache.hs b/Text/Hastache.hs
--- a/Text/Hastache.hs
+++ b/Text/Hastache.hs
@@ -16,14 +16,14 @@
 Simplest example:
 
 @
-import Text.Hastache 
-import Text.Hastache.Context 
-import qualified Data.ByteString.Lazy.Char8 as LZ 
+import           Text.Hastache
+import           Text.Hastache.Context
+import qualified Data.Text.Lazy.IO as TL
 
 main = do 
     res <- hastacheStr defaultConfig (encodeStr template)  
         (mkStrContext context) 
-    LZ.putStrLn res 
+    TL.putStrLn res 
   where 
     template = \"Hello, {{name}}!\\n\\nYou have {{unread}} unread messages.\" 
     context \"name\" = MuVariable \"Haskell\"
@@ -41,22 +41,23 @@
 Using Generics:
 
 @
-import Text.Hastache 
-import Text.Hastache.Context 
-import qualified Data.ByteString.Lazy.Char8 as LZ 
-import Data.Data 
-import Data.Generics 
- 
-data Info = Info { 
-    name    :: String, 
-    unread  :: Int 
+{&#45;\# LANGUAGE DeriveDataTypeable, OverloadedStrings \#&#45;}
+
+import           Text.Hastache
+import           Text.Hastache.Context
+import qualified Data.Text.Lazy.IO as TL
+import           Data.Data
+
+data Info = Info {
+    name    :: String,
+    unread  :: Int
     } deriving (Data, Typeable)
 
-main = do 
-    res <- hastacheStr defaultConfig (encodeStr template) 
-        (mkGenericContext inf) 
-    LZ.putStrLn res 
-  where 
+main = do
+    res <- hastacheStr defaultConfig template
+        (mkGenericContext inf)
+    TL.putStrLn res
+  where
     template = \"Hello, {{name}}!\\n\\nYou have {{unread}} unread messages.\"
     inf = Info \"Haskell\" 100
 @
@@ -71,43 +72,45 @@
     , MuType(..)
     , MuConfig(..)
     , MuVar(..)
+    , composeCtx
     , htmlEscape
     , emptyEscape
     , defaultConfig
     , encodeStr
-    , encodeStrLBS
+    , encodeStrLT
     , decodeStr
-    , decodeStrLBS
+    , decodeStrLT
     ) where 
 
-import Control.Monad (guard, when, mplus, mzero, liftM )
+import Control.Monad (guard, mplus, mzero, liftM )
 import Control.Monad.Reader (ask, runReaderT, MonadReader, ReaderT)
 import Control.Monad.Trans (lift, liftIO, MonadIO)
 import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT)
 import Data.AEq (AEq,(~==))
-import Data.ByteString hiding (map, foldl1)
-import Data.ByteString.Char8 (readInt)
-import Data.Char (ord)
 import Data.Functor ((<$>))
-import Data.Int
 import Data.IORef
+import Data.Int
 import Data.Maybe (isJust)
-import Data.Monoid (mappend, mempty)
+import Data.Monoid (Monoid, mappend, mempty)
+import Data.Text hiding (map, foldl1)
+import Data.Text.IO
+import Data.Version (Version)
 import Data.Word
 import Prelude hiding (putStrLn, readFile, length, drop, tail, dropWhile, elem,
     head, last, reverse, take, span, null)
 import System.Directory (doesFileExist)
 import System.FilePath (combine)
 
-import qualified Blaze.ByteString.Builder as BSB
-import qualified Codec.Binary.UTF8.String as SU
+import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as LZ
-import qualified Data.Foldable as F
 import qualified Data.List as List
-import qualified Data.Text as Text
-import qualified Data.Text.Encoding as TextEncoding
-import qualified Data.Text.Lazy as LText
-import qualified Data.Text.Lazy.Encoding as LTextEncoding
+import qualified Data.Text as T
+import qualified Data.Text.Read as T
+import qualified Data.Text.Encoding as T
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.Builder as TLB
+import qualified Data.Text.Lazy.Encoding as TL
+import qualified Data.Foldable as F
 import qualified Prelude
 
 (~>) :: a -> (a -> b) -> b
@@ -116,72 +119,105 @@
 
 -- | Data for Hastache variable
 type MuContext m =
-    ByteString      -- ^ Variable name
+    Text            -- ^ Variable name
     -> m (MuType m) -- ^ Value
 
+instance (Monad m) => Monoid (MuContext m) where
+    mempty = const $ return MuNothing
+    a `mappend` b = \v -> do
+        x <- a v
+        case x of
+            MuNothing -> b v
+            _ -> return x
+
+-- | Left-leaning compoistion of contexts. Given contexts @c1@ and
+-- @c2@, the behaviour of @(c1 <> c2) x@ is following: if @c1 x@
+-- produces 'MuNothing', then the result is @c2 x@. Otherwise the
+-- result is @c1 x@. Even if @c1 x@ is 'MuNothing', the monadic
+-- effects of @c1@ are still to take place.
+composeCtx :: (Monad m) => MuContext m -> MuContext m -> MuContext m
+composeCtx = mappend
+
 class Show a => MuVar a where
-    -- | Convert to Lazy ByteString
-    toLByteString   :: a -> LZ.ByteString 
+    -- | Convert to lazy 'Data.Text.Lazy.Text'
+    toLText   :: a -> TL.Text
     -- | Is empty variable (empty string, zero number etc.)
-    isEmpty         :: a -> Bool 
+    isEmpty   :: a -> Bool 
     isEmpty _ = False
-        
-instance MuVar ByteString where
-    toLByteString = toLBS
-    isEmpty a = length a == 0
+
+instance MuVar Text where
+    toLText = TL.fromStrict
+    isEmpty = T.null
+
+instance MuVar TL.Text where
+    toLText = id
+    isEmpty a = TL.length a == 0
+
+instance MuVar BS.ByteString where
+    toLText = TL.fromStrict . T.decodeUtf8
+    isEmpty a = BS.length a == 0
     
 instance MuVar LZ.ByteString where
-    toLByteString = id
+    toLText = TL.decodeUtf8
     isEmpty a = LZ.length a == 0
 
-withShowToLBS :: Show a => a -> LZ.ByteString
-withShowToLBS a = show a ~> encodeStr ~> toLBS
+withShowToText :: Show a => a -> TL.Text
+withShowToText a = show a ~> TL.pack
 
 numEmpty :: (Num a,AEq a) => a -> Bool
 numEmpty a = a ~== 0
 
-instance MuVar Integer where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Int     where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Float   where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Double  where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Int8    where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Int16   where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Int32   where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Int64   where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Word    where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Word8   where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Word16  where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Word32  where {toLByteString = withShowToLBS; isEmpty = numEmpty}
-instance MuVar Word64  where {toLByteString = withShowToLBS; isEmpty = numEmpty}
+instance MuVar Integer where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Int     where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Float   where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Double  where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Int8    where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Int16   where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Int32   where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Int64   where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Word    where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Word8   where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Word16  where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Word32  where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar Word64  where {toLText = withShowToText; isEmpty = numEmpty}
+instance MuVar ()      where {toLText = withShowToText}
 
-instance MuVar Text.Text where 
-    toLByteString t = TextEncoding.encodeUtf8 t ~> toLBS
-    isEmpty a = Text.length a == 0
+instance MuVar Version where {toLText = withShowToText }
 
-instance MuVar LText.Text where 
-    toLByteString t = LTextEncoding.encodeUtf8 t
-    isEmpty a = LText.length a == 0
-    
 instance MuVar Char where
-    toLByteString a = (a : "") ~> encodeStr ~> toLBS
+    toLText = TL.singleton
 
 instance MuVar a => MuVar [a] where
-    toLByteString a = toLByteString '[' <+> cnvLst <+> toLByteString ']'
+    toLText a = toLText '[' <+> cnvLst <+> toLText ']'
         where
-        cnvLst = map toLByteString a ~> 
-                LZ.intercalate (toLByteString ',')
-        (<+>) = LZ.append
+        cnvLst = map toLText a ~> 
+                TL.intercalate (toLText ',')
+        (<+>) = TL.append
 
+
+instance MuVar a => MuVar (Maybe a) where
+    toLText (Just a) = toLText a
+    toLText Nothing  = ""
+    isEmpty Nothing  = True
+    isEmpty (Just a) = isEmpty a
+
+instance (MuVar a, MuVar b) => MuVar (Either a b) where
+    toLText (Left  a) = toLText a
+    toLText (Right b) = toLText b
+    isEmpty (Left  a) = isEmpty a
+    isEmpty (Right b) = isEmpty b
+    
+
 instance MuVar [Char] where
-    toLByteString k = k ~> encodeStr ~> toLBS
+    toLText = TL.pack
     isEmpty a = Prelude.length a == 0
 
 data MuType m = 
     forall a. MuVar a => MuVariable a                   |
     MuList [MuContext m]                                |
     MuBool Bool                                         |
-    forall a. MuVar a => MuLambda (ByteString -> a)     |
-    forall a. MuVar a => MuLambdaM (ByteString -> m a)  |
+    forall a. MuVar a => MuLambda (Text -> a)           |
+    forall a. MuVar a => MuLambdaM (Text -> m a)        |
     MuNothing
 
 instance Show (MuType m) where
@@ -193,57 +229,52 @@
     show MuNothing = "MuNothing"
 
 data MuConfig m = MuConfig {
-    muEscapeFunc        :: LZ.ByteString -> LZ.ByteString, 
+    muEscapeFunc        :: TL.Text -> TL.Text, 
         -- ^ Escape function ('htmlEscape', 'emptyEscape' etc.)
     muTemplateFileDir   :: Maybe FilePath,
-        -- ^ Directory for search partial templates ({{> templateName}})
+        -- ^ Directory for search partial templates (@{{> templateName}}@)
     muTemplateFileExt   :: Maybe String,
         -- ^ Partial template files extension
-    muTemplateRead      :: FilePath -> m (Maybe ByteString)
+    muTemplateRead      :: FilePath -> m (Maybe Text)
         -- ^ Template retrieval function. 'Nothing' indicates that the
         --   template could not be found.
     }
 
--- | Convert String to UTF-8 Bytestring
-encodeStr :: String -> ByteString
-encodeStr = pack . SU.encode
-
--- | Convert String to UTF-8 Lazy Bytestring
-encodeStrLBS :: String -> LZ.ByteString
-encodeStrLBS = LZ.pack . SU.encode
+-- | Convert 'String' to 'Text'
+encodeStr :: String -> Text
+encodeStr = T.pack
 
--- | Convert UTF-8 Bytestring to String
-decodeStr :: ByteString -> String
-decodeStr = SU.decode . unpack
+-- | Convert 'String' to Lazy 'Data.Text.Lazy.Text'
+encodeStrLT :: String -> TL.Text
+encodeStrLT = TL.pack
 
--- | Convert UTF-8 Lazy Bytestring to String
-decodeStrLBS :: LZ.ByteString -> String
-decodeStrLBS = SU.decode . LZ.unpack
+-- | Convert 'Text' to 'String'
+decodeStr :: Text -> String
+decodeStr = T.unpack
 
-ord8 :: Char -> Word8
-ord8 = fromIntegral . ord
+-- | Convert Lazy 'Data.Text.Lazy.Text' to 'String'
+decodeStrLT :: TL.Text -> String
+decodeStrLT = TL.unpack
 
+-- | isMuNothing x = x == MuNothing
+isMuNothing :: MuType t -> Bool
 isMuNothing MuNothing = True
 isMuNothing _ = False
 
 -- | Escape HTML symbols
-htmlEscape :: LZ.ByteString -> LZ.ByteString
-htmlEscape str = LZ.unpack str ~> proc ~> LZ.pack
-    where
-    proc :: [Word8] -> [Word8]
-    proc (h:t)
-        | h == ord8 '&' = stp "&amp;" t
-        | h == ord8 '\\'= stp "&#92;" t
-        | h == ord8 '"' = stp "&quot;" t
-        | h == ord8 '\''= stp "&#39;" t
-        | h == ord8 '<' = stp "&lt;" t
-        | h == ord8 '>' = stp "&gt;" t
-        | otherwise     = h : proc t
-    proc [] = []
-    stp a t = map ord8 a ++ proc t
+htmlEscape :: TL.Text -> TL.Text
+htmlEscape = TL.concatMap proc
+  where
+    proc '&'  = "&amp;"
+    proc '\\' = "&#92;"
+    proc '"'  = "&quot;"
+    proc '\'' = "&#39;"
+    proc '<'  = "&lt;"
+    proc '>'  = "&gt;"
+    proc h    = TL.singleton h
 
 -- | No escape
-emptyEscape :: LZ.ByteString -> LZ.ByteString
+emptyEscape :: TL.Text -> TL.Text
 emptyEscape = id
 
 {- | Default config: HTML escape function, current directory as 
@@ -256,62 +287,65 @@
     muTemplateRead = liftIO . defaultTemplateRead
     }
 
-defaultTemplateRead :: FilePath -> IO (Maybe ByteString)
+defaultTemplateRead :: FilePath -> IO (Maybe Text)
 defaultTemplateRead fullFileName = do
     fe <- doesFileExist fullFileName
     if fe
         then Just <$> readFile fullFileName
         else return Nothing
 
-defOTag = "{{" :: ByteString
-defCTag = "}}" :: ByteString
-unquoteCTag = "}}}" :: ByteString
+defOTag = "{{" :: Text
+defCTag = "}}" :: Text
+unquoteCTag = "}}}" :: Text
 
 findBlock :: 
-       ByteString 
-    -> ByteString 
-    -> ByteString
-    -> Maybe (ByteString, Word8, ByteString, ByteString)
+       Text 
+    -> Text 
+    -> Text
+    -> Maybe (Text, Char, Text, Text)
 findBlock str otag ctag = do
     guard (length fnd > length otag)
     Just (pre, symb, inTag, afterClose)
     where
-    (pre, fnd) = breakSubstring otag str
+    (pre, fnd) = breakOn otag str
     symb = index fnd (length otag)
     (inTag, afterClose)
         -- test for unescape ( {{{some}}} )
-        | symb == ord8 '{' && ctag == defCTag = 
-            breakSubstring unquoteCTag fnd ~> \(a,b) -> 
+        | symb == '{' && ctag == defCTag = 
+            breakOn unquoteCTag fnd ~> \(a,b) -> 
             (drop (length otag) a, drop 3 b)
-        | otherwise = breakSubstring ctag fnd ~> \(a,b) -> 
+        | otherwise = breakOn ctag fnd ~> \(a,b) -> 
             (drop (length otag) a, drop (length ctag) b)
 
-toLBS :: ByteString -> LZ.ByteString
-toLBS v = LZ.fromChunks [v]
-
-readVar :: MonadIO m => [MuContext m] -> ByteString -> m LZ.ByteString
-readVar [] _ = return LZ.empty
+readVar :: MonadIO m => [MuContext m] -> Text -> m TL.Text
+readVar [] _ = return TL.empty
 readVar (context:parentCtx) name = do
     muType <- context name
     case muType of
-        MuVariable a -> return $ toLByteString a
-        MuBool a -> return $ show a ~> encodeStr ~> toLBS
+        MuVariable a -> return $ toLText a
+        MuBool a -> return . withShowToText $ a
         MuNothing -> do
           mb <- runMaybeT $ tryFindArrayItem context name
           case mb of
             Just (nctx,nn) -> readVar [nctx] nn
             _ -> readVar parentCtx name
-        _ -> return LZ.empty
+        _ -> return TL.empty
 
+readInt :: Text -> Maybe (Int,Text)
+readInt t = eitherMaybe $ T.decimal t
+  where
+    eitherMaybe (Left _)  = Nothing
+    eitherMaybe (Right x) = Just x
+
 tryFindArrayItem :: MonadIO m => 
        MuContext m
-    -> ByteString
-    -> MaybeT m (MuContext m, ByteString)
+    -> Text
+    -> MaybeT m (MuContext m, Text)
 tryFindArrayItem context name = do
     guard $ length idx > 1
     (idx,nxt) <- MaybeT $ return $ readInt $ tail idx
     guard $ idx >= 0
-    guard $ (null nxt) || ((head nxt) == (ord8 '.'))
+    guard $ (null nxt) || (head nxt == '.')
     muType <- lift $ context nm
     case muType of
         MuList l -> do
@@ -322,87 +356,92 @@
                 else return (ncxt, tail nxt) -- {{some.0.field}}
         _ -> mzero
     where
-    (nm,idx) = breakSubstring dotStr name
+    (nm,idx) = breakOn dotStr name
     dotStr = "."
 
 findCloseSection :: 
-       ByteString 
-    -> ByteString 
-    -> ByteString 
-    -> ByteString
-    -> Maybe (ByteString, ByteString)
+       Text 
+    -> Text 
+    -> Text 
+    -> Text
+    -> Maybe (Text, Text)
 findCloseSection str name otag ctag = do
     guard (length after > 0)
     Just (before, drop (length close) after)
     where
     close = foldl1 append [otag, "/", name, ctag]
-    (before, after) = breakSubstring close str
+    (before, after) = breakOn close str
 
-trimCharsTest :: Word8 -> Bool
-trimCharsTest = (`elem` " \t")
+trimCharsTest :: Char -> Bool
+trimCharsTest = (`Prelude.elem` [' ', '\t'])
 
-trimAll :: ByteString -> ByteString
-trimAll str = span trimCharsTest str ~> snd ~> spanEnd trimCharsTest ~> fst
+trimAll :: Text -> Text
+trimAll = dropAround trimCharsTest
 
-addRes :: MonadIO m => LZ.ByteString -> ReaderT (IORef BSB.Builder) m ()
+addRes :: MonadIO m => (Either T.Text TL.Text) -> ReaderT (IORef TLB.Builder) m ()
 addRes str = do
     rf <- ask
     b <- readIORef rf ~> liftIO
-    let l = mappend b (BSB.fromLazyByteString str)
+    let l = mappend b t
     writeIORef rf l ~> liftIO
     return ()
+  where
+    t = either TLB.fromText TLB.fromLazyText str
 
-addResBS :: MonadIO m => ByteString -> ReaderT (IORef BSB.Builder) m ()
-addResBS str = toLBS str ~> addRes
+addResT :: MonadIO m => T.Text -> ReaderT (IORef TLB.Builder) m ()
+addResT = addRes . Left
 
-addResLZ :: MonadIO m => LZ.ByteString -> ReaderT (IORef BSB.Builder) m ()
-addResLZ = addRes
+addResTL :: MonadIO m => TL.Text -> ReaderT (IORef TLB.Builder) m ()
+addResTL = addRes . Right
 
 processBlock :: MonadIO m => 
-       ByteString 
+       Text 
     -> [MuContext m] 
-    -> ByteString 
-    -> ByteString 
+    -> Text 
+    -> Text 
     -> MuConfig m
-    -> ReaderT (IORef BSB.Builder) m ()
+    -> ReaderT (IORef TLB.Builder) m ()
 processBlock str contexts otag ctag conf = 
     case findBlock str otag ctag of
         Just (pre, symb, inTag, afterClose) -> do
-            addResBS pre
+            addResT pre
             renderBlock contexts symb inTag afterClose otag ctag conf
         Nothing -> do
-            addResBS str
+            addResT str
             return ()
 
+elem :: Char -> Text -> Bool
+elem c = isJust . find (==c)
+
 renderBlock :: MonadIO m =>
        [MuContext m] 
-    -> Word8 
-    -> ByteString 
-    -> ByteString 
-    -> ByteString
-    -> ByteString 
+    -> Char 
+    -> Text 
+    -> Text 
+    -> Text
+    -> Text 
     -> MuConfig m
-    -> ReaderT (IORef BSB.Builder) m ()
+    -> ReaderT (IORef TLB.Builder) m ()
 renderBlock contexts symb inTag afterClose otag ctag conf
     -- comment
-    | symb == ord8 '!' = next afterClose
+    | symb == '!' = next afterClose
     -- unescape variable
-    | symb == ord8 '&' || (symb == ord8 '{' && otag == defOTag) = do
-        addResLZ =<< lift (readVar contexts (tail inTag ~> trimAll))
+    | symb == '&' || (symb == '{' && otag == defOTag) = do
+        addResTL =<< lift (readVar contexts (tail inTag ~> trimAll))
         next afterClose
     -- section, inverted section
-    | symb == ord8 '#' || symb == ord8 '^' = 
+    | symb == '#' || symb == '^' = 
         case findCloseSection afterClose (tail inTag) otag ctag of
             Nothing -> next afterClose
             Just (sectionContent', afterSection') -> 
                 let 
                     dropNL str = 
-                        if length str > 0 && head str == ord8 '\n' 
+                        if length str > 0 && head str == '\n' 
                            then tail str
                            else str
                     sectionContent = dropNL sectionContent'
                     afterSection = 
-                        if ord8 '\n' `elem` sectionContent
+                        if '\n' `elem` sectionContent
                             then dropNL afterSection'
                             else afterSection'
                     tlInTag = tail inTag
@@ -418,7 +457,7 @@
                         next afterSection
                 in do
                 mbCtx <- lift $ runMaybeT readContext
-                if symb == ord8 '#'
+                if symb == '#'
                     then
                       case mbCtx of -- section
                         Just (MuList []) -> next afterSection
@@ -431,11 +470,11 @@
                             else processAndNext
                         Just (MuBool True) -> processAndNext
                         Just (MuLambda func) -> do
-                            func sectionContent ~> toLByteString ~> addResLZ
+                            func sectionContent ~> toLText ~> addResTL
                             next afterSection
                         Just (MuLambdaM func) -> do
                             res <- lift (func sectionContent)
-                            toLByteString res ~> addResLZ
+                            res ~> toLText ~> addResTL
                             next afterSection
                         _ -> next afterSection
                     else case mbCtx of -- inverted section
@@ -447,14 +486,14 @@
                         Nothing -> processAndNext
                         _ -> next afterSection
     -- set delimiter
-    | symb == ord8 '=' = 
+    | symb == '=' = 
         let
             lenInTag = length inTag
             delimitersCommand = take (lenInTag - 1) inTag ~> drop 1
             getDelimiter = do
                 guard $ lenInTag > 4
-                guard $ index inTag (lenInTag - 1) == ord8 '='
-                [newOTag,newCTag] <- Just $ split (ord8 ' ') delimitersCommand
+                guard $ index inTag (lenInTag - 1) == '='
+                [newOTag,newCTag] <- Just $ splitOn (singleton ' ') delimitersCommand
                 Just (newOTag, newCTag)
         in case getDelimiter of
                 Nothing -> next afterClose
@@ -462,7 +501,7 @@
                     processBlock (trim' afterClose) contexts
                         newOTag newCTag conf
     -- partials
-    | symb == ord8 '>' =
+    | symb == '>' =
         let 
             fileName' = tail inTag ~> trimAll
             fileName'' = case muTemplateFileExt conf of
@@ -477,41 +516,40 @@
             next (trim' afterClose)
     -- variable
     | otherwise = do
-        addResLZ . muEscapeFunc conf =<<
+        addResTL . muEscapeFunc conf =<<
           lift (readVar contexts $ trimAll inTag)
         next afterClose
     where
     next t = processBlock t contexts otag ctag conf
     trim' content = 
         dropWhile trimCharsTest content
-        ~> \t -> if length t > 0 && head t == ord8 '\n'
+        ~> \t -> if length t > 0 && head t == '\n'
             then tail t else content
-    processSection = undefined
 
--- | Render Hastache template from ByteString
+-- | Render Hastache template from 'Text'
 hastacheStr :: (MonadIO m) => 
        MuConfig m       -- ^ Configuration
-    -> ByteString       -- ^ Template
+    -> Text             -- ^ Template
     -> MuContext m      -- ^ Context
-    -> m LZ.ByteString
+    -> m TL.Text
 hastacheStr conf str context = 
-    hastacheStrBuilder conf str context >>= return . BSB.toLazyByteString
+    hastacheStrBuilder conf str context >>= return . TLB.toLazyText
 
 -- | Render Hastache template from file
 hastacheFile :: (MonadIO m) => 
        MuConfig m       -- ^ Configuration
     -> FilePath         -- ^ Template file name
     -> MuContext m      -- ^ Context
-    -> m LZ.ByteString
+    -> m TL.Text
 hastacheFile conf file_name context = 
-    hastacheFileBuilder conf file_name context >>= return . BSB.toLazyByteString
+    hastacheFileBuilder conf file_name context >>= return . TLB.toLazyText
 
--- | Render Hastache template from ByteString
+-- | Render Hastache template from 'Text'
 hastacheStrBuilder :: (MonadIO m) => 
        MuConfig m       -- ^ Configuration
-    -> ByteString       -- ^ Template
+    -> Text             -- ^ Template
     -> MuContext m      -- ^ Context
-    -> m BSB.Builder
+    -> m TLB.Builder
 hastacheStrBuilder conf str context = do
     rf <- newIORef mempty ~> liftIO
     runReaderT (processBlock str [context] defOTag defCTag conf) rf
@@ -522,7 +560,7 @@
        MuConfig m       -- ^ Configuration
     -> FilePath         -- ^ Template file name
     -> MuContext m      -- ^ Context
-    -> m BSB.Builder
+    -> m TLB.Builder
 hastacheFileBuilder conf file_name context = do
     str <- readFile file_name ~> liftIO
     hastacheStrBuilder conf str context
diff --git a/Text/Hastache/Context.hs b/Text/Hastache/Context.hs
--- a/Text/Hastache/Context.hs
+++ b/Text/Hastache/Context.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE RankNTypes #-}
 -- Module:      Text.Hastache.Context
 -- Copyright:   Sergey S Lymar (c) 2011-2013 
 -- License:     BSD3
@@ -14,18 +15,24 @@
       mkStrContext
     , mkStrContextM
     , mkGenericContext
+    , mkGenericContext'
+    , Ext
+    , defaultExt
     ) where 
 
 import Data.Data
 import Data.Generics
 import Data.Int
+import Data.Version (Version)
+import Data.Ratio (Ratio)
 import Data.Word
 
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as LBS
 import qualified Data.Map as Map
-import qualified Data.Text as Text
-import qualified Data.Text.Lazy as LText
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Data.Text.Lazy as TL
 
 import Text.Hastache
 
@@ -40,11 +47,19 @@
 mkStrContextM :: Monad m => (String -> m (MuType m)) -> MuContext m
 mkStrContextM f a = decodeStr a ~> f
 
+type Ext = forall b. (Data b, Typeable b) => b -> String
+
+-- | @defaultExt ==@ 'gshow'
+defaultExt :: Ext
+defaultExt = gshow
+
 {- | 
 Make Hastache context from Data.Data deriving type
 
 Supported field types:
 
+ * ()
+
  * String
  
  * Char
@@ -84,13 +99,31 @@
  * Data.Text.Lazy.Text
  
  * Bool
+
+ * Version
+
+ * Maybe @a@ (where @a@ is a supported datatype)
+
+ * Either @a@ @b@ (where @a@ and @b@ are supported datatypes)
+
+ * Data.Text.Text -> Data.Text.Text
+
+ * Data.Text.Text -> Data.Text.Lazy.Text
+
+ * Data.Text.Lazy.Text -> Data.Text.Lazy.Text
  
  * Data.ByteString.ByteString -> Data.ByteString.ByteString
  
  * String -> String
  
  * Data.ByteString.ByteString -> Data.ByteString.Lazy.ByteString
- 
+
+ * MonadIO m => Data.Text.Text -> m Data.Text.Text
+
+ * MonadIO m => Data.Text.Text -> m Data.Text.Lazy.Text
+
+ * MonadIO m => Data.Text.Lazy.Text -> m Data.Text.Lazy.Text 
+
  * MonadIO m => Data.ByteString.ByteString -> m Data.ByteString.ByteString
  
  * MonadIO m => String -> m String
@@ -101,9 +134,10 @@
 
 @
 import Text.Hastache 
-import Text.Hastache.Context 
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as LZ 
+import Text.Hastache.Context
+import qualified Data.Text as T
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.IO as TL
 import Data.Data 
 import Data.Generics 
 import Data.Char
@@ -120,15 +154,15 @@
     simpleListField         :: [String],
     dataListField           :: [InternalData],
     stringFunc              :: String -> String,
-    byteStringFunc          :: B.ByteString -> B.ByteString,
+    textFunc                :: T.Text -> T.Text,
     monadicStringFunc       :: String -> IO String,
-    monadicByteStringFunc   :: B.ByteString -> IO B.ByteString
+    monadicTextFunc         :: T.Text -> IO T.Text
     } deriving (Data, Typeable)
 
 example = hastacheStr defaultConfig (encodeStr template) 
     (mkGenericContext context)
     where
-    template = concat $ map (++ \"\\n\") [
+    template = unlines [
         \"string: {{stringField}}\",
         \"int: {{intField}}\",
         \"data: {{dataField.someField}}, {{dataField.anotherField}}\",
@@ -139,18 +173,18 @@
         \" * {{someField}}, {{anotherField}}. top level var: {{intField}}\",
         \"{{/dataListField}}\",
         \"{{#stringFunc}}upper{{/stringFunc}}\",
-        \"{{#byteStringFunc}}reverse{{/byteStringFunc}}\",
+        \"{{#textFunc}}reverse{{/textFunc}}\",
         \"{{#monadicStringFunc}}upper (monadic){{/monadicStringFunc}}\",
-        \"{{#monadicByteStringFunc}}reverse (monadic){{/monadicByteStringFunc}}\"]
+        \"{{#monadicTextFunc}}reverse (monadic){{/monadicTextFunc}}\"]
     context = Example { stringField = \"string value\", intField = 1, 
         dataField = InternalData \"val\" 123, simpleListField = [\"a\",\"b\",\"c\"],
         dataListField = [InternalData \"aaa\" 1, InternalData \"bbb\" 2],
         stringFunc = map toUpper,
-        byteStringFunc = B.reverse,
+        textFunc = T.reverse,
         monadicStringFunc = return . map toUpper,
-        monadicByteStringFunc = return . B.reverse }
+        monadicTextFunc = return . T.reverse }
 
-main = example >>= LZ.putStrLn
+main = example >>= TL.putStrLn
 @
 
 Result:
@@ -170,15 +204,94 @@
 )cidanom( esrever
 @
 
+Hastache also supports datatypes with multiple constructors:
+
+@
+data A = A { str :: String }
+       | B { num :: Int }
+
+{{#A}}
+A : {{str}}
+{{/A}}
+{{#B}}
+B : {{num}}
+{{/B}}
+@
+
 -}
-                    
-#if MIN_VERSION_base(4,7,0)                    
+#if MIN_VERSION_base(4,7,0)
 mkGenericContext :: (Monad m, Data a, Typeable m) => a -> MuContext m
-#else                    
+#else
 mkGenericContext :: (Monad m, Data a, Typeable1 m) => a -> MuContext m
-#endif                    
-mkGenericContext val = toGenTemp val ~> convertGenTempToContext
-    
+#endif
+mkGenericContext val = toGenTemp id defaultExt val ~> convertGenTempToContext
+
+{-| 
+
+Like 'mkGenericContext', but apply the first function to record field
+names when constructing the context. The second function is used to
+constructing values for context from datatypes that are nor supported
+as primitives in the library. The resulting value can be accessed
+using the @.DatatypeName@ field:
+
+@
+\{\-\# LANGUAGE DeriveDataTypeable \#\-\}
+\{\-\# LANGUAGE FlexibleInstances \#\-\}
+\{\-\# LANGUAGE ScopedTypeVariables \#\-\}
+\{\-\# LANGUAGE StandaloneDeriving \#\-\}
+\{\-\# LANGUAGE TypeSynonymInstances \#\-\}
+import Text.Hastache 
+import Text.Hastache.Context 
+
+import qualified Data.Text.Lazy as TL 
+import qualified Data.Text.Lazy.IO as TL 
+
+import Data.Data (Data, Typeable)
+import Data.Decimal
+import Data.Generics.Aliases (extQ)
+
+data Test = Test {n::Int, m::Decimal} deriving (Data, Typeable)
+deriving instance Data Decimal
+
+val :: Test
+val = Test 1 (Decimal 3 1500)
+
+q :: Ext
+q = defaultExt \`extQ\` (\(i::Decimal) -> "A decimal: " ++ show i)
+
+r "m" = "moo"
+r x   = x
+
+example :: IO TL.Text
+example = hastacheStr defaultConfig
+                      (encodeStr template)
+                      (mkGenericContext' r q val)
+
+template = concat [ 
+     "{{n}}\\n",
+     "{{moo.Decimal}}"
+     ] 
+
+main = example >>= TL.putStrLn
+@
+
+Result:
+
+@
+1
+A decimal: 1.500
+@
+
+-}
+#if MIN_VERSION_base(4,7,0)
+mkGenericContext' :: (Monad m, Data a, Typeable m) 
+                  => (String -> String) -> Ext -> a -> MuContext m
+#else
+mkGenericContext' :: (Monad m, Data a, Typeable1 m) 
+                  => (String -> String) -> Ext -> a -> MuContext m
+#endif
+mkGenericContext' f ext val = toGenTemp f ext val ~> convertGenTempToContext
+
 data TD m = 
       TSimple (MuType m) 
     | TObj [(String, TD m)] 
@@ -186,80 +299,124 @@
     | TUnknown
     deriving (Show)
 
-#if MIN_VERSION_base(4,7,0)                                 
-toGenTemp :: (Data a, Monad m, Typeable m) => a -> TD m
+#if MIN_VERSION_base(4,7,0)
+toGenTemp :: (Data a, Monad m, Typeable m) 
+          => (String -> String) -> Ext -> a -> TD m
 #else
-toGenTemp :: (Data a, Monad m, Typeable1 m) => a -> TD m
-#endif             
-toGenTemp a = zip fields (gmapQ procField a) ~> TObj
+toGenTemp :: (Data a, Monad m, Typeable1 m) 
+          => (String -> String) -> Ext -> a -> TD m
+#endif
+toGenTemp f g a = TObj $ conName : zip fields (gmapQ (procField f g) a) 
     where
-    fields = toConstr a ~> constrFields
+    fields = toConstr a ~> constrFields ~> map f
+    conName = (toConstr a ~> showConstr, TSimple . MuVariable $ g a)
 
-#if MIN_VERSION_base(4,7,0) 
-procField :: (Data a, Monad m, Typeable m) => a -> TD m
+#if MIN_VERSION_base(4,7,0)
+procField :: (Data a, Monad m, Typeable m) 
+          => (String -> String) -> Ext -> a -> TD m
 #else
-procField :: (Data a, Monad m, Typeable1 m) => a -> TD m
+procField :: (Data a, Monad m, Typeable1 m) 
+          => (String -> String) -> Ext -> a -> TD m
 #endif
-procField = 
-    obj
-    `ext1Q` list
-    `extQ` (\(i::String)            -> MuVariable (encodeStr i) ~> TSimple)
-    `extQ` (\(i::Char)              -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Double)            -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Float)             -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Int)               -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Int8)              -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Int16)             -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Int32)             -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Int64)             -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Integer)           -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Word)              -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Word8)             -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Word16)            -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Word32)            -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Word64)            -> MuVariable i ~> TSimple)
-    `extQ` (\(i::BS.ByteString)     -> MuVariable i ~> TSimple)
-    `extQ` (\(i::LBS.ByteString)    -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Text.Text)         -> MuVariable i ~> TSimple)
-    `extQ` (\(i::LText.Text)        -> MuVariable i ~> TSimple)
-    `extQ` (\(i::Bool)              -> MuBool i ~> TSimple)
-    
-    `extQ` muLambdaBSBS
-    `extQ` muLambdaSS
-    `extQ` muLambdaBSLBS
-    
-    `extQ` muLambdaMBSBS
-    `extQ` muLambdaMSS
-    `extQ` muLambdaMBSLBS
-    where
+procField f g a = 
+    case res a of
+      TUnknown -> TSimple . MuVariable . g $ a          
+      b        -> b
+  where
+    res = obj
+        `ext1Q` list
+        `extQ` (\(i::String)            -> MuVariable (encodeStr i) ~> TSimple)
+        `extQ` (\(i::Char)              -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Double)            -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Float)             -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Int)               -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Int8)              -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Int16)             -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Int32)             -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Int64)             -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Integer)           -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Word)              -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Word8)             -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Word16)            -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Word32)            -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Word64)            -> MuVariable i ~> TSimple)
+        `extQ` (\(i::BS.ByteString)     -> MuVariable i ~> TSimple)
+        `extQ` (\(i::LBS.ByteString)    -> MuVariable i ~> TSimple)
+        `extQ` (\(i::T.Text)            -> MuVariable i ~> TSimple)
+        `extQ` (\(i::TL.Text)           -> MuVariable i ~> TSimple)
+        `extQ` (\(i::Bool)              -> MuBool i     ~> TSimple)
+        `extQ` (\()                     -> MuVariable () ~> TSimple)
+        `extQ` (\(i::Version)           -> MuVariable i ~> TSimple)
+        `extQ` muLambdaTT
+        `extQ` muLambdaTTL
+        `extQ` muLambdaTLTL
+        `extQ` muLambdaBSBS
+        `extQ` muLambdaSS
+        `extQ` muLambdaBSLBS
+
+        `extQ` muLambdaMTT
+        `extQ` muLambdaMTTL
+        `extQ` muLambdaMTLTL
+        `extQ` muLambdaMBSBS
+        `extQ` muLambdaMSS
+        `extQ` muLambdaMBSLBS
+
+        `ext1Q` muMaybe
+        `ext2Q` muEither 
+
     obj a = case dataTypeRep (dataTypeOf a) of
-        AlgRep [c] -> toGenTemp a
+        AlgRep (_:_) -> toGenTemp f g a
         _ -> TUnknown
-    list a = map procField a ~> TList
+    list a = map (procField f g) a ~> TList
 
+    muMaybe Nothing = TSimple MuNothing
+    muMaybe (Just a) = TList [procField f g a]
+
+    muEither (Left a) = procField f g a
+    muEither (Right b) = procField f g b
+
+    muLambdaTT :: (T.Text -> T.Text) -> TD m
+    muLambdaTT f = MuLambda f ~> TSimple
+
+    muLambdaTLTL :: (TL.Text -> TL.Text) -> TD m
+    muLambdaTLTL f = MuLambda (f . TL.fromStrict) ~> TSimple
+
+    muLambdaTTL :: (T.Text -> TL.Text) -> TD m
+    muLambdaTTL f = MuLambda f ~> TSimple
+
     muLambdaBSBS :: (BS.ByteString -> BS.ByteString) -> TD m
-    muLambdaBSBS f = MuLambda f ~> TSimple
+    muLambdaBSBS f = MuLambda (f . T.encodeUtf8) ~> TSimple
 
+    muLambdaBSLBS :: (BS.ByteString -> LBS.ByteString) -> TD m
+    muLambdaBSLBS f = MuLambda (f . T.encodeUtf8) ~> TSimple
+
     muLambdaSS :: (String -> String) -> TD m
     muLambdaSS f = MuLambda fd ~> TSimple
         where
         fd s = decodeStr s ~> f
 
-    muLambdaBSLBS :: (BS.ByteString -> LBS.ByteString) -> TD m
-    muLambdaBSLBS f = MuLambda f ~> TSimple
-
     -- monadic
 
+    muLambdaMTT :: (T.Text -> m T.Text) -> TD m
+    muLambdaMTT f = MuLambdaM f ~> TSimple
+
+    muLambdaMTLTL :: (TL.Text -> m TL.Text) -> TD m
+    muLambdaMTLTL f = MuLambdaM (f . TL.fromStrict) ~> TSimple
+
+    muLambdaMTTL :: (T.Text -> m TL.Text) -> TD m
+    muLambdaMTTL f = MuLambdaM f ~> TSimple
+
     muLambdaMBSBS :: (BS.ByteString -> m BS.ByteString) -> TD m
-    muLambdaMBSBS f = MuLambdaM f ~> TSimple
+    muLambdaMBSBS f = MuLambdaM (f . T.encodeUtf8) ~> TSimple
 
+    muLambdaMBSLBS :: (BS.ByteString -> m LBS.ByteString) -> TD m
+    muLambdaMBSLBS f = MuLambdaM (f . T.encodeUtf8) ~> TSimple
+
     muLambdaMSS :: (String -> m String) -> TD m
     muLambdaMSS f = MuLambdaM fd ~> TSimple
         where
         fd s = decodeStr s ~> f
 
-    muLambdaMBSLBS :: (BS.ByteString -> m LBS.ByteString) -> TD m
-    muLambdaMBSLBS f = MuLambdaM f ~> TSimple
 
 convertGenTempToContext :: Monad m => TD m -> MuContext m
 convertGenTempToContext v = mkMap "" Map.empty v ~> mkMapContext
@@ -279,13 +436,13 @@
 
     mkMapContext m a = return $ case Map.lookup a m of
         Nothing ->
-            case a == dotBS of
+            case a == dotT of
                 True -> 
-                    case Map.lookup BS.empty m of
+                    case Map.lookup T.empty m of
                         Nothing -> MuNothing
                         Just a -> a
                 _ -> MuNothing
         Just a -> a
 
-dotBS = encodeStr "."
-
+dotT :: T.Text
+dotT = T.singleton '.'
diff --git a/hastache.cabal b/hastache.cabal
--- a/hastache.cabal
+++ b/hastache.cabal
@@ -1,11 +1,11 @@
 name:            hastache
-version:         0.5.1
+version:         0.6.1
 license:         BSD3
 license-file:    LICENSE
 category:        Text
-copyright:       Sergey S Lymar (c) 2011-2013
+copyright:       Sergey S Lymar (c) 2011-2014
 author:          Sergey S Lymar <sergey.lymar@gmail.com>
-maintainer:      Sergey S Lymar <sergey.lymar@gmail.com>
+maintainer:      Daniil Frumin <dan at covariant.me>
 stability:       experimental
 synopsis:        Haskell implementation of Mustache templates
 cabal-version:   >= 1.8
@@ -22,20 +22,36 @@
     tests/RunTest.sh
     tests/test.hs
     README.md
+    ChangeLog
 
+executable mkReadme
+  main-is: mkReadme.hs
+  build-depends: hastache, process,
+    base >=4 && <4.9
+    ,bytestring
+    ,mtl
+    ,transformers
+    ,directory
+    ,filepath
+    ,text
+    ,containers
+    ,syb
+    ,blaze-builder
+    ,ieee754
+
+
 library
   exposed-modules:
     Text.Hastache
     Text.Hastache.Context
 
   build-depends:
-    base == 4.*
+    base >=4 && <4.9
     ,bytestring
     ,mtl
     ,transformers
     ,directory
     ,filepath
-    ,utf8-string
     ,text
     ,containers
     ,syb
@@ -53,7 +69,7 @@
 
   build-depends:
     hastache
-   ,base == 4.*
+   ,base >=4 && <4.9
    ,directory
    ,mtl
    ,HUnit
diff --git a/mkReadme.hs b/mkReadme.hs
new file mode 100644
--- /dev/null
+++ b/mkReadme.hs
@@ -0,0 +1,50 @@
+#!/usr/local/bin/runhaskell
+import Text.Hastache 
+import Text.Hastache.Context
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.IO as TL
+import System.Process (readProcess)
+import System.Directory (setCurrentDirectory, getCurrentDirectory)
+import System.FilePath ((</>))
+import Data.List (span, lines, intersperse)
+import Data.Char (isSpace)
+
+main = do 
+    res <- hastacheFile defaultConfig "README.md.ha"
+        (mkStrContext context)
+    TL.writeFile "README.md" res 
+    where 
+    context "example" = MuLambdaM $ \fn -> do
+        cd <- setExampleDir
+        fc <- readFile $ exampleFile fn
+        let { forTC = case span (\t -> trim t /= be) (lines $ trim fc) of
+            (a,[]) -> a
+            (_,a) -> drop 1 a }
+        let explText = concat $ intersperse "\n" forTC
+        
+        setCurrentDirectory cd
+        return $ concat [
+              "```haskell\n"
+            , explText
+            , "\n```"
+            ]
+
+    context "runExample" = MuLambdaM $ \fn -> do
+        cd <- setExampleDir
+        explRes <- readProcess "runhaskell" [exampleFile fn] []
+        setCurrentDirectory cd
+        return $ concat [
+              "```\n"
+            , trim explRes
+            , "\n```"
+            ]
+
+    be = "-- begin example"
+    setExampleDir = do
+        cd <- getCurrentDirectory
+        setCurrentDirectory $ cd </> "examples"
+        return cd
+    exampleFile fn = decodeStr fn ++ ".hs"
+
+trim :: String -> String
+trim = f . f where f = reverse . dropWhile isSpace
diff --git a/tests/test.hs b/tests/test.hs
--- a/tests/test.hs
+++ b/tests/test.hs
@@ -15,13 +15,14 @@
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as LZ
 import qualified Data.Text as T
+import qualified Data.Text.Lazy as TL
 
 resCorrectness = "result correctness"
 
 -- Hastache comments
 commentsTest = do
     res <- hastacheStr defaultConfig (encodeStr template) undefined
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \hello {{! comment #1}} world! \n\
@@ -38,7 +39,7 @@
 variablesTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkStrContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \   Char:               [ {{Char}} ]            \n\
@@ -75,7 +76,7 @@
 showHideSectionsTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkStrContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \no context : {{^noCtx}}Should render{{/noCtx}}\n\
@@ -129,7 +130,7 @@
 listSectionTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkStrContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \text 1\n\
@@ -155,7 +156,7 @@
 boolSectionTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkStrContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \text 1\n\
@@ -188,14 +189,14 @@
 lambdaSectionTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkStrContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \text 1\n\
         \{{#function}}Hello{{/function}}\n\
         \text 2\n\
         \"
-    context "function" = MuLambda BS.reverse
+    context "function" = MuLambda T.reverse
 
     testRes = "\
         \text 1\n\
@@ -206,7 +207,7 @@
 -- Transform section with monadic function
 lambdaMSectionTest = do
     (res, writerState) <- runWriterT monadicFunction
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     assertEqualStr "monad state correctness" (decodeStr writerState)
         testMonad
     where
@@ -218,7 +219,7 @@
         \"
     context "mf" = MuLambdaM $ \i -> do
         tell i
-        return $ BS.reverse i
+        return $ T.reverse i
     testRes = "\
         \[cba]\n\
         \[fed]\n\
@@ -231,7 +232,7 @@
         (mkStrContextM context)
     let { res = case r of
         Left err  -> "error: " ++ err
-        Right res -> decodeStrLBS res }
+        Right res -> decodeStrLT res }
     assertEqualStr resCorrectness res testRes
     where
     template = "Hello, {{name}}! You have {{unread}} unread messages. {{some}}"
@@ -244,7 +245,7 @@
 setDelimiterTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkStrContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \text 1\n\
@@ -269,7 +270,7 @@
 partialsTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkStrContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \text 1\n\
@@ -298,7 +299,8 @@
     someMuLambdaBS      :: BS.ByteString -> BS.ByteString,
     someMuLambdaS       :: String -> String,
     someMuLambdaMBS     :: BS.ByteString -> IO BS.ByteString,
-    someMuLambdaMS      :: String -> IO String
+    someMuLambdaMS      :: String -> IO String,
+    someEitherValue     :: Either String Int
     }
     deriving (Data, Typeable)
 
@@ -306,7 +308,7 @@
 genericContextTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkGenericContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \text 1\n\
@@ -328,6 +330,7 @@
         \{{#someMuLambdaS}}upper{{/someMuLambdaS}}\n\
         \{{#someMuLambdaMBS}}reverse in IO lambda{{/someMuLambdaMBS}}\n\
         \{{#someMuLambdaMS}}upper in IO lambda{{/someMuLambdaMS}}\n\
+        \{{someEitherValue}}\n\
         \text 2\n\
         \"
     context = SomeData {
@@ -340,7 +343,8 @@
         someMuLambdaBS = BS.reverse,
         someMuLambdaS = map toUpper,
         someMuLambdaMBS = return . BS.reverse,
-        someMuLambdaMS = return . map toUpper
+        someMuLambdaMS = return . map toUpper,
+        someEitherValue = Right 123
         }
 
     testRes = "\
@@ -361,6 +365,7 @@
         \UPPER\n\
         \adbmal OI ni esrever\n\
         \UPPER IN IO LAMBDA\n\
+        \123\n\
         \text 2\n\
         \"
 
@@ -368,7 +373,7 @@
 nestedContextTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkStrContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \{{top}}\n\
@@ -403,7 +408,7 @@
 
 nestedGenericContextTest = do
     res <- hastacheStr defaultConfig (encodeStr template) context
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \Top variable : {{topDataTop}}\n\
@@ -429,11 +434,42 @@
         \Nested variable : NESTED_TWO\n\
         \"
 
+-- Nested generic context with polymorphic datatypes
+data Person = Person { personName :: String } deriving (Data, Typeable)
+data Info = Info { person :: Maybe Person } deriving (Data, Typeable)
+data Infos = Infos { infos :: [Info] } deriving (Data, Typeable)
+
+nestedPolyGenericContextTest = do
+    res <- hastacheStr defaultConfig (encodeStr template) context
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
+    where
+    datum = Infos [Info Nothing, Info $ Just $ Person "Dude", Info $ Just $ Person "Dude2"]
+    template = "Greetings: {{#infos}}{{#person}}Hello, {{personName}}!\n{{/person}}{{/infos}}"
+    context = mkGenericContext datum
+    testRes = "Greetings: Hello, Dude!\nHello, Dude2!\n"
+
+-- Generic context with custom extension
+data MyData = MyData Int deriving (Data, Typeable)
+data TestInfo = TestInfo {n::Int,m::MyData} deriving (Data, Typeable)
+
+testExt :: Ext
+testExt = defaultExt `extQ` (\(MyData i) -> "Data " ++ show i)
+
+genericExtTest = do
+    res <- hastacheStr defaultConfig (encodeStr template) context
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
+    where
+    datum = TestInfo 1 (MyData 0)
+    template = "{{n}}\n{{m.MyData}}"
+    context = mkGenericContext' id testExt datum
+    testRes = "1\nData 0"
+
+
 -- Accessing array item by index
 arrayItemsTest = do
     res <- hastacheStr defaultConfig (encodeStr template)
         (mkStrContext context)
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \{{section.0.name}} {{section.1.name}} {{section.2.name}}\n\
@@ -464,7 +500,7 @@
 
 arrayItemsTestGeneric = do
     res <- hastacheStr defaultConfig (encodeStr template) context
-    assertEqualStr resCorrectness (decodeStrLBS res) testRes
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
     where
     template = "\
         \{{items.0.name}} {{items.2.name}}\n\
@@ -480,6 +516,49 @@
         \Bob Alice\n\
         \"
 
+-- Multiple constructors in generic contexts
+data Hero = Good { goodness :: Int }
+          | Evil { evilness :: Int }
+          deriving (Data, Typeable)
+
+data Heroes = Heroes { heroes :: [Hero] }
+            deriving (Data, Typeable)
+
+multipleConstrTest = do
+    res <- hastacheStr defaultConfig (encodeStr template)
+        (mkGenericContext context)
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
+    where
+    context  = Heroes [Good 4, Evil 2]
+    template = "\
+        \{{#heroes}}\
+        \{{#Good}}{{goodness}}{{/Good}}\
+        \{{#Evil}}{{evilness}}{{/Evil}}\
+        \{{/heroes}}\
+        \"
+    testRes = "42"
+
+-- Context composition
+compositionTest = do
+    res <- hastacheStr defaultConfig (encodeStr template) $
+        mkGenericContext context 
+        `composeCtx` mempty
+        `composeCtx` mkStrContext context2
+
+    assertEqualStr resCorrectness (decodeStrLT res) testRes
+    where
+    context  = Heroes [Good 4, Evil 2]
+    context2 "Ugly" = MuVariable (3::Int)
+    context2 _      = MuNothing
+    template = "\
+        \{{#heroes}}\
+        \{{#Good}}{{goodness}}{{/Good}}\
+        \{{#Evil}}{{evilness}}{{/Evil}}\
+        \{{#Ugly}}{{Ugly}}{{/Ugly}}\
+        \{{/heroes}}\
+        \"
+    testRes = "4323"
+
 tests = TestList [
       TestLabel "Comments test" (TestCase commentsTest)
     , TestLabel "Variables test" (TestCase variablesTest)
@@ -492,12 +571,19 @@
     , TestLabel "Set delimiter test" (TestCase setDelimiterTest)
     , TestLabel "Partials test" (TestCase partialsTest)
     , TestLabel "Generic context test" (TestCase genericContextTest)
+    , TestLabel "Multiple constructors in a generic context"
+        (TestCase multipleConstrTest)    
     , TestLabel "Nested context test" (TestCase nestedContextTest)
     , TestLabel "Nested generic context test"
                                         (TestCase nestedGenericContextTest)
+
+    , TestLabel "Nested generic context with polymorphic datatypes test"
+                                        (TestCase nestedPolyGenericContextTest)
     , TestLabel "Accessing array item by index" (TestCase arrayItemsTest)
     , TestLabel "Accessing array item by index (generic version)"
         (TestCase arrayItemsTestGeneric)
+    , TestLabel "Composing contexts" (TestCase compositionTest)
+    , TestLabel "Generic contexts with custom extensions" (TestCase genericExtTest)
     ]
 
 main = do
