diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,7 @@
+0.2.0.0
+=======
+
+  * Added `sh_` quasiquoter to avoid type annotations for trivial quotes
+  * Moved `Eval` onto `Text` to speed I/O up.
+  * Quasiquoters support literals and constructors with no arguments
+  * More `Embed` instances
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+# command-qq
+[![Hackage](https://budueba.com/hackage/command-qq)](http://hackage.haskell.org/package/command-qq)
+[![Build Status](https://secure.travis-ci.org/biegunka/command-qq.png?branch=master)](http://travis-ci.org/biegunka/command-qq)
+[![Build Status](https://drone.io/github.com/biegunka/command-qq/status.png)](https://drone.io/github.com/biegunka/command-qq/latest)
+
+```
+>>> import System.Command.QQ
+>>> putStr =<< unlines . reverse . lines <$> [sh|cowsay "Hello, I am command-qq!"|]
+                ||     ||
+                ||----w |
+            (__)\       )\/\
+         \  (oo)\_______
+        \   ^__^
+ -------------------------
+< Hello, I am command-qq! >
+ _________________________
+```
+
+## Install
+
+```
+% cabal install command-qq
+```
+
+## Features
+
+### Quasiquotation syntax for external interpreters
+
+```
+>>> [sh_| echo hello world! |]
+hello world!
+```
+
+### Custom quasiquoters
+
+```
+ghci = quoter $ callCommand "ghc" ["-ignore-dot-ghci", "-e"]
+```
+
+Then you can use `ghci` in ghci!
+
+```
+>>> [ghci| putStrLn "hello world!" |] :: IO ()
+hello world!
+```
+
+For more examples, see [`examples/CustomQQ.hs`][0]
+
+### Haskell values embedding
+
+Let's define `Embed` instance for a custom data type:
+
+```haskell
+data Bang = Bang
+
+instance Embed Bang where
+  embed Bang = "!"
+```
+
+Then you can use variables of `Bang` type in quoted strings!
+
+```
+>>> [sh_| echo hello#{Bang} |]
+hello!
+>>> let bang = Bang in [sh_| echo hello#{bang} |]
+hello!
+```
+
+Note, `command-qq` does not support full Haskell in embeddings,
+only variables/constructors names and literals
+
+### DSLs
+
+See [`examples/CommandT.hs`][1]
+
+  [0]: https://github.com/biegunka/command-qq/blob/master/examples/CustomQQ.hs
+  [1]: https://github.com/biegunka/command-qq/blob/master/examples/CommandT.hs
diff --git a/command-qq.cabal b/command-qq.cabal
--- a/command-qq.cabal
+++ b/command-qq.cabal
@@ -1,36 +1,33 @@
 name:                command-qq
-version:             0.1.0.0
+version:             0.2.0.0
 synopsis:            Quasiquoters for external commands
 description:
   Features:
   .
-  * Quasiquotation syntax for external commands:
+  * Quasiquotation syntax for external interpreters
   .
-  > [sh| echo hello world! |]
+    > >>> [sh_| echo hello world! |]
+    > hello world!
   .
-  * Easy way to define custom quasiquoters (see @examples/CustomQQ.hs@)
+  * Custom quasiquoters
   .
-  > ghci = QQ.quoter $ QQ.callCommand "ghc" ["-ignore-dot-ghci", "-e"]
+    > ghci = quoter $ callCommand "ghc" ["-ignore-dot-ghci", "-e"]
   .
-  * Can embed Haskell variables in scripts
+    Then you can use @ghci@ in ghci!
   .
-  > class Embed a where
-  >   embed :: a -> String
-  >
-  > instance Embed String
-  >   embed = id
-  >
-  > main :: IO ()
-  > main = let excl = replicate 3 '!' in -- I'd love to show an example but haddock markup fails here
+    > >>> [ghci| putStrLn "hello world!" |] :: IO ()
+    > hello world!
   .
-  * Can embed quasiquoters in DSLs (see @examples/CommandT.hs@)
+    For more examples, see @examples/CustomQQ.hs@
   .
-  > class Eval r where
-  >   eval :: String -> [String] -> r
-  >
-  > instance Eval (IO ExitCode) where
-  >   eval command args = System.Process.rawSystem command args
+  * Haskell values embedding
   .
+    See README.md for an example
+  .
+  * DSLs
+  .
+    See @examples/CommandT.hs@
+  .
 homepage:            http://biegunka.github.io/command-qq/
 license:             BSD3
 license-file:        LICENSE
@@ -40,6 +37,8 @@
 build-type:          Simple
 cabal-version:       >= 1.10
 extra-source-files:
+  CHANGELOG.md
+  README.md
   examples/CommandT.hs
   examples/CustomQQ.hs
 
@@ -47,19 +46,22 @@
 library
   default-language:  Haskell2010
   build-depends:
-    base == 4.*,
-    process,
-    template-haskell,
-    unix
+      base == 4.*
+    , process
+    , template-haskell
+    , text
   hs-source-dirs:    src
-  exposed-modules:   System.Command.QQ
+  exposed-modules:
+    System.Command.QQ
+    System.Command.QQ.Embed
+    System.Command.QQ.Eval
 
 test-suite doctests
   default-language:  Haskell2010
   type:              exitcode-stdio-1.0
   build-depends:
-    base == 4.*,
-    doctest
+      base == 4.*
+    , doctest
   hs-source-dirs:    tests
   main-is:           Doctests.hs
 
@@ -67,15 +69,18 @@
   default-language:  Haskell2010
   type:              exitcode-stdio-1.0
   build-depends:
-    base == 4.*,
-    hspec,
-    command-qq,
-    template-haskell
+      base == 4.*
+    , hspec
+    , command-qq
+    , template-haskell
+    , text
   hs-source-dirs:
-    tests,
-    examples
+      tests
+    , examples
   main-is:           Spec.hs
-  other-modules:     EmbeddingSpec
+  other-modules:
+    System.Command.QQ.EmbedSpec
+    System.Command.QQ.EvalSpec
 
 
 source-repository head
diff --git a/examples/CommandT.hs b/examples/CommandT.hs
--- a/examples/CommandT.hs
+++ b/examples/CommandT.hs
@@ -15,17 +15,16 @@
 import Control.Monad.IO.Class (MonadIO(..))        -- transformers
 import Control.Monad.Trans.Class (MonadTrans(..))  -- transformers
 import Control.Monad.Trans.Either                  -- either
-import Data.Functor.Alt (Alt(..))                  -- semigroupoids
-import Data.Functor.Apply (Apply(..))              -- semigroupoids
-import Data.Functor.Bind (Bind(..))                -- semigroupoids
 import Data.Monoid (Last(..))                      -- base
+import Data.Text.Lazy (Text)                       -- text
 import System.Exit (ExitCode(..))                  -- base
 import System.Command.QQ (Eval(..))                -- command-qq
 
 -- $setup
 -- >>> :set -XQuasiQuotes
 -- >>> import System.Command.QQ
--- >>> let lengths = [sh|while read line; do echo ${#line}; done|] :: String -> CommandT IO String
+-- >>> import qualified Data.Text.Lazy as T
+-- >>> let lengths = [sh|while read line; do echo ${#line}; done|] :: Text -> CommandT IO Text
 
 
 infixl 1 >>! -- same as >>=
@@ -53,7 +52,7 @@
 newtype CommandT m a = CommandT { unCommandT :: EitherT (Last Failure) m a }
 
 -- | Failed command with exit code and @stderr@
-data Failure = Failure Command Int String
+data Failure = Failure Command Int Text
     deriving (Show, Read)
 
 -- | Command name and arguments
@@ -67,26 +66,17 @@
 instance Monad m => Functor (CommandT m) where
   fmap f (CommandT x) = CommandT (fmap f x)
 
-instance Monad m => Apply (CommandT m) where
-  CommandT f <.> CommandT x = CommandT (f <.> x)
-
 instance Monad m => Applicative (CommandT m) where
   pure = CommandT . pure
-  (<*>) = (<.>)
-
-instance Monad m => Bind (CommandT m) where
-  CommandT x >>- k = CommandT (x >>- unCommandT . k)
+  CommandT f <*> CommandT x = CommandT (f <*> x)
 
 instance Monad m => Monad (CommandT m) where
   return = pure
-  (>>=) = (>>-)
-
-instance Monad m => Alt (CommandT m) where
-  CommandT f <!> CommandT x = CommandT (f <!> x)
+  CommandT x >>= k = CommandT (x >>= unCommandT . k)
 
 instance Monad m => Alternative (CommandT m) where
   empty = CommandT empty
-  (<|>) = (<!>)
+  CommandT f <|> CommandT x = CommandT (f <|> x)
 
 instance Monad m => MonadPlus (CommandT m) where
   mzero = empty
@@ -98,14 +88,14 @@
 instance MonadIO m => MonadIO (CommandT m) where
   liftIO = lift . liftIO
 
-instance (o ~ String, MonadIO m) => Eval (CommandT m o) where
+instance (o ~ Text, MonadIO m) => Eval (CommandT m o) where
   eval command args = CommandT . EitherT $ do
     (status, out, err) <- liftIO $ eval command args
     return $ case status of
       ExitSuccess   -> Right out
       ExitFailure i -> Left (Last (Just (Failure (Command command args) i err)))
 
-instance (i ~ String, o ~ String, MonadIO m) => Eval (i -> CommandT m o) where
+instance (i ~ Text, o ~ Text, MonadIO m) => Eval (i -> CommandT m o) where
   eval command args input = CommandT . EitherT $ do
     (status, out, err) <- liftIO $ eval command args input
     return $ case status of
@@ -127,9 +117,9 @@
 --
 -- And playing may involve arbitrary Haskell functions, of course:
 --
--- >>> runCommandT $ [sh|echo -e "hello\nworld!!!">&2; exit 1|] >>! lengths . unlines . reverse . lines
+-- >>> runCommandT $ [sh|echo -e "hello\nworld!!!">&2; exit 1|] >>! lengths . T.unlines . reverse . T.lines
 -- Right "8\n5\n"
-(>>!) :: Monad m => CommandT m a -> (String -> CommandT m b) -> CommandT m b
+(>>!) :: Monad m => CommandT m a -> (Text -> CommandT m b) -> CommandT m b
 x >>! k = CommandT . EitherT $ do
   t <- runCommandT x
   case t of
diff --git a/src/System/Command/QQ.hs b/src/System/Command/QQ.hs
--- a/src/System/Command/QQ.hs
+++ b/src/System/Command/QQ.hs
@@ -1,86 +1,93 @@
-{-# LANGUAGE DefaultSignatures #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE ViewPatterns #-}
 -- | Quasiquoters for external commands
 module System.Command.QQ
   ( -- * Quasiquoters
-    sh, shell, interpreter
+    -- ** Default shell
+    sh_, sh
+    -- ** Constructors
+  , shell, interpreter
     -- * Customizations
   , quoter, callCommand
-  , Eval(..), Embed(..)
+  , module System.Command.QQ.Embed
+  , module System.Command.QQ.Eval
   ) where
 
-import           Control.Applicative ((<$), pure)
-import           Data.Int
-import           Data.Word
-import           Language.Haskell.TH
-import           Language.Haskell.TH.Quote
-import           System.Exit (ExitCode)
-import           System.Posix.Env (getEnvDefault)
-import qualified System.Process as P
+import Control.Applicative
+import Data.Char (isLower, isUpper)
+import Data.Maybe (fromMaybe)
+import Language.Haskell.TH
+import Language.Haskell.TH.Quote
+import System.Environment (lookupEnv)
+import Text.Read (readMaybe)
 
+import System.Command.QQ.Embed
+import System.Command.QQ.Eval
+
 -- $setup
 -- >>> :set -XQuasiQuotes
+-- >>> :set -XOverloadedStrings
+-- >>> import System.Exit
+-- >>> import Data.Text.Lazy (Text)
 
 
 -- | Quasiquoter for the default shell
 --
--- \"default\" here means it uses value of @SHELL@ environment variable
--- or @\/bin\/sh@ if it is not set.
+-- Constructs polymorphic action of type @Eval a => a@ from passed string.
 --
--- >>> [sh|echo "hi!"|] :: IO ExitCode
--- hi!
+-- Uses @SHELL@ environment variable as path to shell executable
+-- or @\/bin\/sh@ if it is unset.
+--
+-- >>> [sh|echo "hello, world!"|] :: IO ExitCode
 -- ExitSuccess
--- >>> [sh|echo "hi!"|] :: IO String
--- "hi!\n"
+-- >>> [sh|echo "hello, world!"|] :: IO Text
+-- "hello, world!\n"
 --
 -- Haskell values can be embedded with Ruby-like syntax:
 --
 -- >>> let apples = 7
--- >>> [sh|echo "#{apples} apples!"|] :: IO String
+-- >>> [sh|echo "#{apples} apples!"|] :: IO Text
 -- "7 apples!\n"
---
--- Works only for expressions (obviously):
---
--- >>> return 3 :: IO [sh|blah|]
--- <BLANKLINE>
--- <interactive>:116:16:
---     Exception when trying to run compile-time code:
---       this quasiquoter does not support splicing types
---       Code: quoteType sh "blah"
 sh :: QuasiQuoter
 sh = quoter $ \string -> do
-  shellEx <- runIO $ getEnvDefault "SHELL" "/bin/sh"
+  shellEx <- runIO $ getEnvDefault "/bin/sh" "SHELL"
   callCommand shellEx ["-c"] string
 
--- | Shell commands quasiquoter maker
+-- | Simple quasiquoter for the default shell
 --
--- \"Shell\" here means something that implements the following interface:
+-- 'sh' analog that always constructs an action of type
+-- @IO ()@ and so can always be used without type annotations
 --
+-- >>> [sh_|echo "hello, world!"|]
+-- hello, world!
+sh_ :: QuasiQuoter
+sh_ = quoter $ \string -> do
+  shellEx <- runIO $ getEnvDefault "/bin/sh" "SHELL"
+  callCommand_ shellEx ["-c"] string
+
+-- | Shell's quasiquoter constructor
+--
+-- \"Shell\" here means executable that has the following API:
+--
 -- @
 -- \<SHELL\> -c \<COMMAND\>
 -- @
 --
 -- /e.g./ @sh@, @bash@, @zsh@, @ksh@, @tcsh@, @python@, etc
---
--- Everything that applies to 'sh' applies to 'shell'
 shell :: FilePath -> QuasiQuoter
 shell path = quoter (callCommand path ["-c"])
 
--- | Interpreter commands quasiquoter maker
+-- | Interpreter's quasiquoter constructor
 --
--- \"Interpreter\" here means something that implements the following interface:
+-- \"Interpreter\" here means executable that has the following API:
 --
 -- @
 -- \<INTERPRETER\> -e \<COMMAND\>
 -- @
 --
 -- /e.g./ @perl@, @ruby@, @ghc@, etc
---
--- Everything that applies to 'sh' applies to 'interpreter'
 interpreter :: FilePath -> QuasiQuoter
 interpreter path = quoter (callCommand path ["-e"])
 
@@ -110,139 +117,43 @@
 callCommand path args string =
   [e| eval path (args ++ [$(string2exp string)]) |]
 
+-- | Construct Haskell expression for external command call
+callCommand_
+  :: FilePath -- ^ Command path
+  -> [String] -- ^ Arguments that go to command before quasiquoter contents
+  -> String   -- ^ Quasiquoter contents
+  -> Q Exp
+callCommand_ path args string =
+  [e| eval path (args ++ [$(string2exp string)]) :: IO () |]
+
 -- | Parse references to Haskell variables
 string2exp :: String -> Q Exp
 string2exp = raw where
+  raw, var :: String -> Q Exp
   raw (break (== '#') -> parts) = case parts of
     (before, '#':'{' :after) -> [e| before ++ $(var after) |]
     (before, '#':'\\':after) -> [e| before ++ '#' : $(raw after) |]
     (before, '#':after)      -> [e| before ++ '#' : $(raw after) |]
     (before, [])             -> [e| before |]
-    _                        -> fail $ "Should never happen"
+    _                        -> fail "Should never happen"
 
   var (break (== '}') -> parts) = case parts of
-     (before, '}':after) -> [e| embed $(return (VarE (mkName before))) ++ $(raw after) |]
-     (before, _)         -> fail $ "Bad variable pattern: #{" ++ before
-
-
--- | Different interesting return types for quasiquoters
---
--- Instances here mostly resemble the types of things in "System.Process"
-class Eval r where
-  eval :: String -> [String] -> r
-
--- | Most basic instance: nothing is known about what happened in external command
---
--- >>> [sh|echo hello world|] :: IO ()
--- hello world
-instance Eval (IO ()) where
-  eval command args = () <$ P.rawSystem command args
-
--- | Return only exit code of external process
---
--- >>> [sh|echo hello world|] :: IO ExitCode
--- hello world
--- ExitSuccess
---
--- >>> [sh|exit 1|] :: IO ExitCode
--- ExitFailure 1
-instance Eval (IO ExitCode) where
-  eval command args = P.rawSystem command args
-
--- | Return only stdout of external process
---
--- Does not care if external process failed.
---
--- >>> [sh|echo hello world|] :: IO String
--- "hello world\n"
---
--- >>> [sh|echo hello world; return 1|] :: IO String
--- "hello world\n"
-instance Eval (IO String) where
-  eval command args = do
-    (_, out, _) <- eval command args
-    return out
-
--- | Return exit code, stdout, and stderr of external process
---
--- >>> [sh|echo hello world; echo bye world >&2; exit 1|] :: IO (ExitCode, String, String)
--- (ExitFailure 1,"hello world\n","bye world\n")
-instance
-  ( s ~ ExitCode
-  , o ~ String
-  , e ~ String
-  ) => Eval (IO (s, o, e)) where
-  eval command args = P.readProcessWithExitCode command args ""
-
--- | Return exit code, stdout, and stderr of external process
--- and consume stdin from supplied 'String'
---
--- >>> [sh|while read line; do echo ${#line}; done|] "hello\nworld!\n"
--- (ExitSuccess,"5\n6\n","")
-instance
-  ( i ~ String
-  , o ~ (ExitCode, String, String)
-  ) => Eval (i -> IO o) where
-  eval command args stdin = P.readProcessWithExitCode command args stdin
-
-
--- | Embed haskell values into external commands
---
--- I recommend using @-XExtendedDefaultRules@ for modules
--- where you want to embed values, it would save for annoying
--- type annotations for numeric literals
---
--- @
--- embed . embed = embed
--- @
-class Embed a where
-  embed :: a -> String
-  default embed :: Show a => a -> String
-  embed = show
-
--- |
--- >>> embed 4
--- "4"
---
--- >>> embed (7 :: Integer)
--- "7"
-instance Embed Integer
-
--- |
--- >>> embed (7 :: Int)
--- "7"
-instance Embed Int
-instance Embed Int8
-instance Embed Int16
-instance Embed Int32
-instance Embed Int64
-instance Embed Word
-instance Embed Word8
-instance Embed Word16
-instance Embed Word32
-instance Embed Word64
-
--- |
--- >>> embed (7 :: Float)
--- "7.0"
-instance Embed Float
-
--- |
--- >>> embed 4.0
--- "4.0"
---
--- >>> embed (7 :: Double)
--- "7.0"
-instance Embed Double
+     (b:efore, '}':after)
+        | isLower b                    -> external (VarE (mkName (b:efore))) after
+        | isUpper b                    -> external (ConE (mkName (b:efore))) after
+        | isUpper b                    -> external (ConE (mkName (b:efore))) after
+        | Just i <- readMaybe (b:efore) -> external (LitE (IntegerL i)) after
+        | Just d <- readMaybe (b:efore) -> external (LitE (RationalL (toRational (d :: Double)))) after
+        | Just c <- readMaybe (b:efore) -> external (LitE (CharL c)) after
+        | Just s <- readMaybe (b:efore) -> external (LitE (StringL s)) after
+     (before, _)                       -> fail $ "Invalid name: " ++ before
 
--- |
--- >>> embed 'c'
--- "c"
-instance Embed Char where
-  embed = pure
+  external :: Exp -> String -> Q Exp
+  external e after = [e| embed $(return e) ++ $(raw after) |]
 
--- |
--- >>> embed "hi"
--- "hi"
-instance Embed String where
-  embed = id
+-- | Get environment variable or default value if it's unset
+getEnvDefault
+  :: String -- ^ The default vefault
+  -> String -- ^ Environment variable
+  -> IO String
+getEnvDefault def query = fromMaybe def <$> lookupEnv query
diff --git a/src/System/Command/QQ/Embed.hs b/src/System/Command/QQ/Embed.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Command/QQ/Embed.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE DefaultSignatures #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeFamilies #-}
+-- | Haskell values embedding
+module System.Command.QQ.Embed
+  ( Embed(..)
+  ) where
+
+import Control.Applicative
+import Data.Int
+import Data.Ratio (Ratio)
+import Data.Word
+import Foreign.C.Types
+
+-- $setup
+-- >>> import Data.Ratio
+
+
+-- | Embed haskell values into external commands
+--
+-- I recommend using @-XExtendedDefaultRules@ for modules
+-- where you want to embed values, it would save for annoying
+-- type annotations for numeric literals
+--
+-- @
+-- embed . embed = embed
+-- @
+class Embed a where
+  embed :: a -> String
+  default embed :: Show a => a -> String
+  embed = show
+
+instance Embed Integer
+instance Embed Int
+instance Embed Int8
+instance Embed Int16
+instance Embed Int32
+instance Embed Int64
+instance Embed Word
+instance Embed Word8
+instance Embed Word16
+instance Embed Word32
+instance Embed Word64
+instance Embed Float
+instance Embed Double
+
+instance Embed CChar
+instance Embed CSChar
+instance Embed CUChar
+instance Embed CShort
+instance Embed CUShort
+instance Embed CInt
+instance Embed CUInt
+instance Embed CLong
+instance Embed CULong
+instance Embed CSize
+instance Embed CLLong
+instance Embed CULLong
+instance Embed CFloat
+instance Embed CDouble
+
+-- |
+-- >>> embed (3 % 5)
+-- "0.6"
+instance a ~ Integer => Embed (Ratio a) where
+  embed = embed . (fromRational :: Rational -> Double)
+
+-- |
+-- >>> embed 'c'
+-- "c"
+instance Embed Char where
+  embed = pure
+
+-- |
+-- >>> embed "hi"
+-- "hi"
+instance Embed String where
+  embed = id
diff --git a/src/System/Command/QQ/Eval.hs b/src/System/Command/QQ/Eval.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Command/QQ/Eval.hs
@@ -0,0 +1,118 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeFamilies #-}
+-- | Evalute passed arguments with external interpreter
+module System.Command.QQ.Eval
+  ( Eval(..)
+  ) where
+
+import           Control.Applicative
+import           Control.Concurrent
+import           Control.Exception (evaluate)
+import           Control.Monad
+import           Data.Text.Lazy (Text)
+import qualified Data.Text.Lazy as T
+import qualified Data.Text.Lazy.IO as T
+import           System.Exit (ExitCode)
+import qualified System.Process as P
+import           System.IO (hFlush, hClose)
+
+-- $setup
+-- >>> import System.Command.QQ
+
+
+-- | Different interesting return types for quasiquoters
+--
+-- Instances here mostly resemble the types of things in "System.Process"
+class Eval r where
+  eval :: String -> [String] -> r
+
+-- | Most basic instance: nothing is known about what happened in external command
+--
+-- >>> [sh|echo hello world|] :: IO ()
+-- hello world
+instance Eval (IO ()) where
+  eval command args = () <$ P.rawSystem command args
+
+-- | Return exit code of the external process
+--
+-- >>> [sh|exit 0|] :: IO ExitCode
+-- ExitSuccess
+--
+-- >>> [sh|exit 7|] :: IO ExitCode
+-- ExitFailure 7
+instance Eval (IO ExitCode) where
+  eval command args = do
+    (s, _, _) <- eval command args (T.pack "")
+    return s
+
+-- | Return stdout of the external process as 'Text'
+--
+-- Does not care whether external process has failed or not.
+--
+-- >>> [sh|echo -n hello world|] :: IO Text
+-- "hello world"
+instance Eval (IO Text) where
+  eval command args = do
+    (_, o, _) <- eval command args
+    return o
+
+-- | Return stdout of external process as 'String'
+--
+-- Does not care whether external process has failed or not.
+--
+-- >>> [sh|echo -n hello world|] :: IO String
+-- "hello world"
+instance Eval (IO String) where
+  eval command args = T.unpack <$> eval command args
+
+-- | Return exit code, stdout, and stderr of external process
+--
+-- >>> [sh|echo hello world; echo bye world >&2; exit 1|] :: IO (ExitCode, Text, Text)
+-- (ExitFailure 1,"hello world\n","bye world\n")
+instance
+  ( s ~ ExitCode
+  , o ~ Text
+  , e ~ Text
+  ) => Eval (IO (s, o, e)) where
+  eval command args = eval command args (T.pack "")
+
+-- | Return exit code, stdout, and stderr of the external process
+-- and pass supplied 'Text' to its stdin
+--
+-- >>> [sh|while read line; do echo ${#line}; done|] "hello\nworld!\n"
+-- (ExitSuccess,"5\n6\n","")
+instance
+  ( i ~ Text
+  , o ~ (ExitCode, Text, Text)
+  ) => Eval (i -> IO o) where
+  eval = readProcessWithExitCode
+
+readProcessWithExitCode :: String -> [String] -> Text -> IO (ExitCode, Text, Text)
+readProcessWithExitCode cmd args input = do
+    (Just ih, Just oh, Just eh, p) <-
+        P.createProcess (P.proc cmd args)
+          { P.std_in  = P.CreatePipe
+          , P.std_out = P.CreatePipe
+          , P.std_err = P.CreatePipe
+          }
+
+    m <- newEmptyMVar
+    o <- T.hGetContents oh
+    e <- T.hGetContents eh
+
+    forkFinally (evaluate (T.length o)) (\_ -> putMVar m ())
+    forkFinally (evaluate (T.length e)) (\_ -> putMVar m ())
+
+    unless (T.null input) $ do
+      T.hPutStr ih input
+      hFlush ih
+    hClose ih
+
+    takeMVar m
+    takeMVar m
+    hClose oh
+    hClose eh
+
+    s <- P.waitForProcess p
+
+    return (s, o, e)
diff --git a/tests/Doctests.hs b/tests/Doctests.hs
--- a/tests/Doctests.hs
+++ b/tests/Doctests.hs
@@ -6,6 +6,8 @@
 main :: IO ()
 main = doctest
   [ "src/System/Command/QQ.hs"
+  , "src/System/Command/QQ/Embed.hs"
+  , "src/System/Command/QQ/Eval.hs"
   , "examples/CustomQQ.hs"
   , "examples/CommandT.hs"
   ]
diff --git a/tests/EmbeddingSpec.hs b/tests/EmbeddingSpec.hs
deleted file mode 100644
--- a/tests/EmbeddingSpec.hs
+++ /dev/null
@@ -1,51 +0,0 @@
-{-# LANGUAGE ExtendedDefaultRules #-}
-{-# LANGUAGE QuasiQuotes #-}
-{-# OPTIONS_GHC -fno-warn-unused-binds #-}
-module EmbeddingSpec where
-
-import System.Command.QQ
-import Test.Hspec
-
-
-spec :: Spec
-spec = do
-  describe "variable embeddings" $ do
-
-    it "can embed integers" $
-      let foo = 7 in [sh|echo #{foo}|] `shouldReturn` "7\n"
-
-    it "can embed doubles" $
-      let foo = 7.0 in [sh|echo #{foo}|] `shouldReturn` "7.0\n"
-
-    it "can embed characters" $
-      let foo = 'z' in [sh|echo #{foo}|] `shouldReturn` "z\n"
-
-    it "can embed strings" $
-      let foo = "hello" in [sh|echo #{foo}|] `shouldReturn` "hello\n"
-
-  describe "multi-line embeddings" $ do
-
-    it "supports multiline commands" $
-      [sh|
-        echo hello
-        echo world
-        echo !!!
-      |] `shouldReturn` "hello\nworld\n!!!\n"
-
-    it "supports embeddings in multiline commands" $
-      let foo = 4
-          bar = 7
-      in [sh|
-        echo #{foo}
-        echo #{bar}
-      |] `shouldReturn` "4\n7\n"
-
-  describe "escapings" $ do
-
-    it "is possible to write #{} in scripts still (as a comment)" $ do
-      [sh|echo #\{foo}|] `shouldReturn` "\n"
-      [sh|echo #\\{foo}|] `shouldReturn` "\n"
-
-    it "is possible to write #{} in scripts still (as a string)" $ do
-      [sh|echo "#\{foo}"|] `shouldReturn` "#{foo}\n"
-      [sh|echo "#\\{foo}"|] `shouldReturn` "#\\{foo}\n"
diff --git a/tests/System/Command/QQ/EmbedSpec.hs b/tests/System/Command/QQ/EmbedSpec.hs
new file mode 100644
--- /dev/null
+++ b/tests/System/Command/QQ/EmbedSpec.hs
@@ -0,0 +1,105 @@
+module System.Command.QQ.EmbedSpec (spec) where
+
+import Data.Int
+import Data.Ratio
+import Data.Word
+import Foreign.C.Types
+import System.Command.QQ
+import Test.Hspec
+
+
+spec :: Spec
+spec =
+  describe "embedding" $ do
+    describe "Haskell types" $ do
+      describe "numeric types" $ do
+        it "embeds Integer" $
+          embed (4 :: Integer) `shouldBe` "4"
+
+        it "embeds Int" $
+          embed (4 :: Int) `shouldBe` "4"
+
+        it "embeds Int8" $
+          embed (4 :: Int8) `shouldBe` "4"
+
+        it "embeds Int16" $
+          embed (4 :: Int16) `shouldBe` "4"
+
+        it "embeds Int32" $
+          embed (4 :: Int32) `shouldBe` "4"
+
+        it "embeds Int64" $
+          embed (4 :: Int64) `shouldBe` "4"
+
+        it "embeds Word" $
+          embed (4 :: Word) `shouldBe` "4"
+
+        it "embeds Word8" $
+          embed (4 :: Word8) `shouldBe` "4"
+
+        it "embeds Word16" $
+          embed (4 :: Word16) `shouldBe` "4"
+
+        it "embeds Word32" $
+          embed (4 :: Word32) `shouldBe` "4"
+
+        it "embeds Word64" $
+          embed (4 :: Word64) `shouldBe` "4"
+
+        it "embeds Float" $
+          embed (7 :: Float) `shouldBe` "7.0"
+
+        it "embeds Double" $
+          embed (7 :: Double) `shouldBe` "7.0"
+
+        it "embeds Rational" $
+          embed (3 % 5) `shouldBe` "0.6"
+
+      it "embeds Char" $
+        embed 'e' `shouldBe` "e"
+
+      it "embeds String" $
+        embed "foo" `shouldBe` "foo"
+
+    describe "C types" $ do
+      it "embeds CChar" $
+        embed (4 :: CChar) `shouldBe` "4"
+
+      it "embeds CSChar" $
+        embed (4 :: CSChar) `shouldBe` "4"
+
+      it "embeds CUChar" $
+        embed (4 :: CUChar) `shouldBe` "4"
+
+      it "embeds CShort" $
+        embed (4 :: CShort) `shouldBe` "4"
+
+      it "embeds CUShort" $
+        embed (4 :: CUShort) `shouldBe` "4"
+
+      it "embeds CInt" $
+        embed (4 :: CInt) `shouldBe` "4"
+
+      it "embeds CUInt" $
+        embed (4 :: CUInt) `shouldBe` "4"
+
+      it "embeds CLong" $
+        embed (4 :: CLong) `shouldBe` "4"
+
+      it "embeds CULong" $
+        embed (4 :: CULong) `shouldBe` "4"
+
+      it "embeds CSize" $
+        embed (4 :: CSize) `shouldBe` "4"
+
+      it "embeds CLLong" $
+        embed (4 :: CLLong) `shouldBe` "4"
+
+      it "embeds CULLong" $
+        embed (4 :: CULLong) `shouldBe` "4"
+
+      it "embeds CFloat" $
+        embed (7 :: CFloat) `shouldBe` "7.0"
+
+      it "embeds CDouble" $
+        embed (7 :: CDouble) `shouldBe` "7.0"
diff --git a/tests/System/Command/QQ/EvalSpec.hs b/tests/System/Command/QQ/EvalSpec.hs
new file mode 100644
--- /dev/null
+++ b/tests/System/Command/QQ/EvalSpec.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE ExtendedDefaultRules #-}
+{-# LANGUAGE QuasiQuotes #-}
+module System.Command.QQ.EvalSpec (spec) where
+
+import Data.Text.Lazy (Text, pack)
+import System.Command.QQ
+import System.Exit (ExitCode(..))
+import Test.Hspec
+
+
+spec :: Spec
+spec = do
+  describe "quasiquotation" $ do
+    context "unicode" $ do
+      it "works with unicode symbols in the String output" $
+        [sh|echo -n "ДМИТРИЙ МАЛИКОВ"|] `shouldReturn` "ДМИТРИЙ МАЛИКОВ"
+
+      it "works with unicode symbols in the Text output" $
+        [sh|echo -n "ЕГОР ЛЕТОВ"|] `shouldReturn` text "ЕГОР ЛЕТОВ"
+
+    context "exit code" $ do
+      it "is possible to get successful exit code" $
+        [sh|exit 0|] `shouldReturn` ExitSuccess
+
+      it "is possible to get unsuccessful exit code" $
+        [sh|exit 4|] `shouldReturn` ExitFailure 4
+
+    it "is possible to get all of exitcode, stdout, and stderr" $
+      [sh|echo -n hello; echo -n world >&2; exit 4|] `shouldReturn`
+        (ExitFailure 4, text "hello", text "world")
+
+  describe "embedding" $ do
+    describe "variable embeddings" $ do
+
+      it "can embed integers" $
+        let foo = 7 in [sh|echo #{foo}|] `shouldReturn` "7\n"
+
+      it "can embed doubles" $
+        let foo = 7.0 in [sh|echo #{foo}|] `shouldReturn` "7.0\n"
+
+      it "can embed characters" $
+        let foo = 'z' in [sh|echo #{foo}|] `shouldReturn` "z\n"
+
+      it "can embed strings" $
+        let foo = "hello" in [sh|echo #{foo}|] `shouldReturn` "hello\n"
+
+    describe "multi-line embeddings" $ do
+      it "supports multiline commands" $
+        [sh|
+          echo hello
+          echo world
+          echo !!!
+        |] `shouldReturn` "hello\nworld\n!!!\n"
+
+      it "supports embeddings in multiline commands" $
+        let foo = 4
+            bar = 7
+        in [sh|
+          echo #{foo}
+          echo #{bar}
+        |] `shouldReturn` "4\n7\n"
+
+    describe "escapings" $ do
+      it "is possible to write #{} literally as a comment" $ do
+        [sh|echo #\{foo}|] `shouldReturn` "\n"
+        [sh|echo #\\{foo}|] `shouldReturn` "\n"
+
+      it "is possible to write #{} literally as a string" $ do
+        [sh|echo "#\{foo}"|] `shouldReturn` "#{foo}\n"
+        [sh|echo "#\\{foo}"|] `shouldReturn` "#\\{foo}\n"
+
+    describe "literals" $ do
+      it "is possible to embed integer literals" $
+        [sh|echo -n #{4}|] `shouldReturn` "4"
+
+      it "is possible to embed rational literals" $
+        [sh|echo -n #{4.0}|] `shouldReturn` "4.0"
+
+      it "is possible to embed char literals" $
+        [sh|echo -n #{'q'}|] `shouldReturn` "q"
+
+      it "is possible to embed string literals" $
+        [sh|echo -n #{"hello"}|] `shouldReturn` "hello"
+
+    describe "custom data types" $
+      it "is possible to embed custom data types" $
+        [sh|echo -n hello#{Bang}|] `shouldReturn` "hello!"
+
+data Bang = Bang
+
+instance Embed Bang where embed Bang = "!"
+
+text :: String -> Text
+text = pack
