diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@
 
 ## Parse Functions
 
-`HasSalak` monad provide a unified function `require` to parse properties. Here are some examples.
+`MonadSalak` monad provide a unified function `require` to parse properties. Here are some examples.
 
 ```Haskell
 a :: Bool              <- require "bool.key"
diff --git a/bench/SalakBench.hs b/bench/SalakBench.hs
new file mode 100644
--- /dev/null
+++ b/bench/SalakBench.hs
@@ -0,0 +1,43 @@
+module SalakBench(main) where
+
+import           Control.Monad.Reader
+import           Criterion.Main
+import           Data.Default
+import           Data.Text            (Text)
+import           Salak
+
+
+main = do
+  sp <- loadAndRunSalak' (loadMock vals) return
+  let run :: forall a. ReaderT SourcePack IO a -> IO a
+      run = flip runReaderT sp
+  v  <- run $ require "int" :: IO (IO Int)
+  defaultMain
+    [ bgroup "load"
+      [ bench "loadMock" $ whnfIO $ loadAndRunSalak (loadMock vals) action
+      , bench "loadCMD"  $ whnfIO $ loadAndRunSalak (loadCommandLine def) action
+      , bench "loadEnv"  $ whnfIO $ loadAndRunSalak loadEnv action
+      ]
+    , bgroup "parse-io"
+      [ bench "read-io"  $ whnfIO v
+      , bench "parse"    $ whnfIO (run $ require "int" :: IO (IO Int))
+      ]
+    , bgroup "parse-int"
+      [ bench "int"      $ whnfIO (run $ require "int" :: IO Int)
+      , bench "int/text" $ whnfIO (run $ require "int" :: IO Text)
+      , bench "int/bool" $ whnfIO (run $ require "int" :: IO (Either String Bool))
+      ]
+    , bgroup "run"
+      [ bench "text"     $ whnfIO $ (run $ require "a.b.c.d.e.f.g.h.i.j.k.text" :: IO Text)
+      , bench "bool"     $ whnfIO $ (run $ require "a.b.c.d.e.f.g.h.i.j.k.bool" :: IO Bool)
+      ]
+    ]
+
+action = return ()
+
+vals =
+  [ ("int",  "128")
+  , ("a.b.c.d.e.f.g.h.i.j.k.text", "xxx")
+  , ("a.b.c.d.e.f.g.h.i.j.k.bool", "true")
+  , ("empty", "")
+  ]
diff --git a/salak.cabal b/salak.cabal
--- a/salak.cabal
+++ b/salak.cabal
@@ -1,6 +1,6 @@
 cabal-version: 1.12
 name: salak
-version: 0.3.4
+version: 0.3.4.1
 license: MIT
 license-file: LICENSE
 copyright: 2019 Daniel YU
@@ -27,11 +27,10 @@
         Salak.Internal.Writable
     default-language: Haskell2010
     default-extensions: RecordWildCards TupleSections OverloadedStrings
-                        ScopedTypeVariables FlexibleInstances MultiParamTypeClasses
-                        DeriveGeneric
+                        NoOverloadedLists ScopedTypeVariables FlexibleInstances
+                        MultiParamTypeClasses DeriveGeneric FlexibleContexts RankNTypes
     ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures
     build-depends:
-        attoparsec >=0.13.2.2 && <0.14,
         base >=4.10 && <5,
         bytestring >=0.10.8.2 && <0.11,
         containers >=0.6.0.1 && <0.7,
@@ -42,6 +41,7 @@
         filepath >=1.4.2.1 && <1.5,
         hashable >=1.2.7.0 && <1.3,
         heaps >=0.3.6.1 && <0.4,
+        megaparsec >=7.0.5 && <7.1,
         menshen >=0.0.3 && <0.1,
         mtl >=2.2.2 && <2.3,
         scientific >=0.3.6.2 && <0.4,
@@ -50,16 +50,16 @@
         unliftio-core >=0.1.2.0 && <0.2,
         unordered-containers >=0.2.10.0 && <0.3
 
-test-suite spec
+test-suite salak-spec
     type: exitcode-stdio-1.0
-    main-is: Spec.hs
+    main-is: SalakSpec.hs
     build-tool-depends: hspec-discover:hspec-discover -any
     hs-source-dirs: test src
     other-modules:
         Salak.Internal.KeySpec
         Salak.Internal.PropSpec
         Salak.Internal.SourceSpec
-        Salak.Internal.TrieSpec
+        Salak.TrieSpec
         Salak
         Salak.Internal
         Salak.Internal.Key
@@ -71,12 +71,12 @@
         Paths_salak
     default-language: Haskell2010
     default-extensions: RecordWildCards TupleSections OverloadedStrings
-                        ScopedTypeVariables FlexibleInstances MultiParamTypeClasses
-                        DeriveGeneric
+                        NoOverloadedLists ScopedTypeVariables FlexibleInstances
+                        MultiParamTypeClasses DeriveGeneric FlexibleContexts RankNTypes
     ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures
+                 -main-is SalakSpec.main -rtsopts -threaded -with-rtsopts=-K1K
     build-depends:
         QuickCheck >=2.13.2 && <2.14,
-        attoparsec >=0.13.2.2 && <0.14,
         base >=4.10 && <5,
         bytestring >=0.10.8.2 && <0.11,
         containers >=0.6.0.1 && <0.7,
@@ -88,9 +88,44 @@
         hashable >=1.2.7.0 && <1.3,
         heaps >=0.3.6.1 && <0.4,
         hspec ==2.*,
+        megaparsec >=7.0.5 && <7.1,
         menshen >=0.0.3 && <0.1,
         mtl >=2.2.2 && <2.3,
         random ==1.1.*,
+        scientific >=0.3.6.2 && <0.4,
+        text >=1.2.3.1 && <1.3,
+        time >=1.8.0.2 && <1.9,
+        unliftio-core >=0.1.2.0 && <0.2,
+        unordered-containers >=0.2.10.0 && <0.3
+
+benchmark salak-bench
+    type: exitcode-stdio-1.0
+    main-is: SalakBench.hs
+    hs-source-dirs: bench
+    other-modules:
+        Paths_salak
+    default-language: Haskell2010
+    default-extensions: RecordWildCards TupleSections OverloadedStrings
+                        NoOverloadedLists ScopedTypeVariables FlexibleInstances
+                        MultiParamTypeClasses DeriveGeneric FlexibleContexts RankNTypes
+    ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures
+                 -main-is SalakBench.main -rtsopts -threaded -with-rtsopts=-K1K
+    build-depends:
+        base >=4.10 && <5,
+        bytestring >=0.10.8.2 && <0.11,
+        containers >=0.6.0.1 && <0.7,
+        criterion >=1.5.5.0 && <1.6,
+        data-default >=0.7.1.1 && <0.8,
+        directory >=1.3.3.0 && <1.4,
+        dlist >=0.8.0.7 && <0.9,
+        exceptions >=0.10.2 && <0.11,
+        filepath >=1.4.2.1 && <1.5,
+        hashable >=1.2.7.0 && <1.3,
+        heaps >=0.3.6.1 && <0.4,
+        megaparsec >=7.0.5 && <7.1,
+        menshen >=0.0.3 && <0.1,
+        mtl >=2.2.2 && <2.3,
+        salak -any,
         scientific >=0.3.6.2 && <0.4,
         text >=1.2.3.1 && <1.3,
         time >=1.8.0.2 && <1.9,
diff --git a/src/Salak.hs b/src/Salak.hs
--- a/src/Salak.hs
+++ b/src/Salak.hs
@@ -9,7 +9,7 @@
 -- |
 -- Module:      Salak
 -- Copyright:   2019 Daniel YU
--- License:     BSD3
+-- License:     MIT
 -- Maintainer:  leptonyu@gmail.com
 -- Stability:   experimental
 -- Portability: portable
@@ -100,8 +100,8 @@
     True
     False
     defaultParseCommandLine
-    putStrLn
     (\_ -> return ())
+    (\_ -> return ())
 
 data FileConfig = FileConfig
   { configNm  :: Maybe String
@@ -109,6 +109,7 @@
   }
 
 instance FromProp m FileConfig where
+  {-# INLINE fromProp #-}
   fromProp = FileConfig
     <$> "name" .?= Nothing
     <*> "dir"  .?= Nothing
@@ -129,6 +130,7 @@
 loadByExt :: HasLoad a => a -> FilePath -> LoadSalak ()
 loadByExt xs f = mapM_ go (loaders xs)
   where
+    {-# INLINE go #-}
     go (ext, ly) = tryLoadFile ly $ f ++ "." ++ ext
 
 -- | Default load salak.
@@ -154,16 +156,14 @@
     , ifS searchHome    getHomeDirectory
     ] (loadConf $ fromMaybe configName configNm)
   where
+    {-# INLINE ifS #-}
     ifS True gxd = Just <$> liftIO gxd
     ifS _    _   = return Nothing
+    {-# INLINE loadConf #-}
     loadConf n mf = lift mf >>= mapM_ (liftNT . loadExt . (</> n))
 
 loadSalakWith :: (MonadThrow m, MonadIO m, HasLoad file) => file -> String -> LoadSalakT m ()
 loadSalakWith file name = loadSalak def { configName = name, loadExt = loadByExt file }
-
--- | Standard salak functions, by load and run with `RunSalakT`.
-loadAndRunSalak :: (MonadThrow m, MonadIO m) => LoadSalakT m () -> RunSalakT m a -> m a
-loadAndRunSalak lstm ma = loadAndRunSalak' lstm $ runRun ma
 
 -- | Run salak, load strategy refer to `loadSalak`
 runSalak :: (MonadCatch m, MonadIO m) => PropConfig -> RunSalakT m a -> m a
diff --git a/src/Salak/Internal.hs b/src/Salak/Internal.hs
--- a/src/Salak/Internal.hs
+++ b/src/Salak/Internal.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE BangPatterns               #-}
 {-# LANGUAGE DeriveFunctor              #-}
 {-# LANGUAGE FlexibleContexts           #-}
 {-# LANGUAGE FlexibleInstances          #-}
@@ -14,7 +15,7 @@
 -- |
 -- Module:      Salak.Internal
 -- Copyright:   2019 Daniel YU
--- License:     BSD3
+-- License:     MIT
 -- Maintainer:  leptonyu@gmail.com
 -- Stability:   experimental
 -- Portability: portable
@@ -23,13 +24,13 @@
 --
 module Salak.Internal(
     loadAndRunSalak'
+  , loadAndRunSalak
   , loadTrie
   , loadList
   , LoadSalakT
   , LoadSalak
   , RunSalakT
   , RunSalak
-  , runRun
   , MonadSalak(..)
   , loadMock
   , loadEnv
@@ -63,6 +64,7 @@
 import qualified Control.Monad.IO.Unlift as IU
 import           Control.Monad.Reader
 import qualified Control.Monad.State     as MS
+import           Data.Char               (toLower)
 import           Data.HashMap.Strict     (HashMap)
 import qualified Data.HashMap.Strict     as HM
 import           Data.Maybe
@@ -89,15 +91,17 @@
   }
 
 -- | Configuration Loader Monad, used for load properties from sources. Custom loaders using `loadTrie`
-newtype LoadSalakT  m a = LoadSalakT (MS.StateT UpdateSource m a)
-  deriving (Functor, Applicative, Monad, MonadTrans, MonadIO, MS.MonadState UpdateSource, MonadThrow, MonadCatch)
+newtype LoadSalakT m a = LoadSalakT (MS.StateT UpdateSource m a)
+  deriving (Functor, Applicative, Monad, MonadIO, MonadTrans, MS.MonadState UpdateSource, MonadThrow, MonadCatch)
 
 -- | Simple IO Monad
 type LoadSalak = LoadSalakT IO
 
+{-# INLINE runLoad #-}
 runLoad :: Monad m => LoadSalakT m a -> UpdateSource -> m a
 runLoad (LoadSalakT ma) = MS.evalStateT ma
 
+{-# INLINE liftNT #-}
 liftNT :: MonadIO m => LoadSalak () -> LoadSalakT m ()
 liftNT a = MS.get >>= liftIO . runLoad a
 
@@ -108,9 +112,7 @@
     liftIO $ void $ swapMVar lfunc f
   logSalak msg = do
     UpdateSource{..} <- MS.get
-    liftIO $ do
-      f <- readMVar lfunc
-      f msg
+    liftIO $ readMVar lfunc >>= ($ msg)
 
 instance (MonadThrow m, IU.MonadUnliftIO m) => IU.MonadUnliftIO (LoadSalakT m) where
   askUnliftIO = do
@@ -118,41 +120,38 @@
     lift $ IU.withUnliftIO $ \u -> return (IU.UnliftIO (IU.unliftIO u . flip runLoad ut))
 
 -- | Standard `MonadSalak` instance.
-newtype RunSalakT m a = RunSalakT (ReaderT SourcePack m a)
+newtype RunSalakT m a = RunSalakT { runSalakT :: ReaderT SourcePack m a }
   deriving (Functor, Applicative, Monad, MonadTrans, MonadIO, MonadReader SourcePack, MonadThrow, MonadCatch)
 
 -- | Simple IO Monad
 type RunSalak = RunSalakT IO
 
-runRun :: Monad m => RunSalakT m a -> SourcePack -> m a
-runRun (RunSalakT ma) = runReaderT ma
-
-
 instance MonadIO m => MonadSalak (RunSalakT m) where
   askSourcePack = ask
 
 instance (MonadThrow m, IU.MonadUnliftIO m) => IU.MonadUnliftIO (RunSalakT m) where
   askUnliftIO = do
     ut <- ask
-    lift $ IU.withUnliftIO $ \u -> return (IU.UnliftIO (IU.unliftIO u . flip runRun ut))
+    lift $ IU.withUnliftIO $ \u -> return (IU.UnliftIO (IU.unliftIO u . flip runReaderT ut . runSalakT))
 
 -- | Basic loader
 loadTrie :: (MonadThrow m, MonadIO m) => Bool -> String -> (Int -> IO TraceSource) -> LoadSalakT m ()
-loadTrie canReload name f = do
+loadTrie !canReload !name f = do
   logSalak $ "Loading " ++ (if canReload then "[reloadable]" else "") ++ name
   UpdateSource{..} <- MS.get
-  v              <- liftIO $ readMVar ref
-  ts             <- liftIO $ loadSource f refNo (fmap ([],) v)
-  let (t,_,es) = extract v ts
-  if null es
-    then do
-      liftIO $ modifyMVar_ update $ \u -> go ts u refNo
-      let nut = UpdateSource ref (refNo + 1) (HM.insert refNo name refMap) lfunc qfunc update
-      _ <- liftIO $ swapMVar ref t
-      MS.put nut
-    else throwM $ PropException $ unlines es
+  (MS.put=<<) $ liftIO $ do
+    v  <- readMVar ref
+    ts <- loadSource f refNo (fmap ([],) v)
+    let (t,_,es) = extract v ts
+    if null es
+      then do
+        modifyMVar_ update $ go ts refNo
+        _ <- swapMVar ref t
+        return $ UpdateSource ref (refNo + 1) (HM.insert refNo name refMap) lfunc qfunc update
+      else throwM $ PropException $ unlines es
   where
-    go ts ud n = return $ do
+    {-# INLINE go #-}
+    go ts n ud = return $ do
       (c,d) <- ud
       c1    <- loadSource (if canReload then f else (\_ -> return ts)) n c
       return (c1,d)
@@ -166,24 +165,33 @@
 loadAndRunSalak' :: (MonadThrow m, MonadIO m) => LoadSalakT m () -> (SourcePack -> m a) -> m a
 loadAndRunSalak' lstm f = load lstm >>= f
 
+-- | Standard salak functions, by load and run with `RunSalakT`.
+loadAndRunSalak :: (MonadThrow m, MonadIO m) => LoadSalakT m () -> RunSalakT m a -> m a
+loadAndRunSalak lstm = loadAndRunSalak' lstm . runReaderT . runSalakT
+
 load :: (MonadThrow m, MonadIO m) => LoadSalakT m () -> m SourcePack
 load lm = do
-  r <- liftIO $ newMVar T.empty
-  q <- liftIO $ newMVar $ \s -> Right $ void $ swapMVar r s
-  u <- liftIO $ newMVar $ return (T.empty, return ())
-  l <- liftIO $ newMVar $ \_ -> return ()
-  runLoad (lm >> MS.get) (UpdateSource r 0 HM.empty l q u) >>= toSourcePack
+  us <- liftIO $ do
+    r <- newMVar T.empty
+    q <- newMVar $ \s -> Right $ void $ swapMVar r s
+    u <- newMVar $ return (T.empty, return ())
+    l <- newMVar $ \_ -> return ()
+    return $ UpdateSource r 0 HM.empty l q u
+  runLoad (lm >> MS.get) us >>= toSourcePack
 
+{-# INLINE toSourcePack #-}
 toSourcePack :: MonadIO m => UpdateSource -> m SourcePack
-toSourcePack UpdateSource{..} = liftIO (readMVar ref) >>= \s -> return $ SourcePack s s S.empty mempty qfunc lfunc go
-  where
-    go = do
+toSourcePack UpdateSource{..} = liftIO $ do
+  s <- readMVar ref
+  return
+    $ SourcePack s s S.empty mempty qfunc lfunc
+    $ do
       t        <- readMVar ref
       (ts, ac) <- join $ readMVar update
-      let (s,cs,es) = extract t ts
+      let (t1,cs,es) = extract t ts
       f <- readMVar qfunc
       if null es
-        then case f s of
+        then case f t1 of
           Left  e -> return (ReloadResult True $ lines e)
           Right a -> ac >> a >> return (ReloadResult False $ lines $ show cs)
         else return (ReloadResult True es)
@@ -196,9 +204,16 @@
 loadEnv :: (MonadThrow m, MonadIO m) => LoadSalakT m ()
 loadEnv = loadList False "environment" go
   where
-    go = concatMap split2 . filter ((/= '_') . head . fst) <$> getEnvironment
-    split2 (k,v) = [(TT.pack k,v),(convert k,v)]
-    convert = TT.toLower . TT.pack . map (\c -> if c == '_' then '.' else c)
+    {-# INLINE go #-}
+    go = fmap split2 . filter ((/= '_') . head . fst) <$> getEnvironment
+    {-# INLINE split2 #-}
+    split2 (k,v) = (convert k, mkValue' $ TT.pack v)
+    {-# INLINE mkValue' #-}
+    mkValue' v = case mkValue (VT v) of
+      Left  _ -> VR [VRT v]
+      Right x -> x
+    {-# INLINE convert #-}
+    convert = TT.pack . map (\c -> if c == '_' then '.' else toLower c)
 
 -- | Convert arguments to properties
 type ParseCommandLine = [String] -> IO [(Text, Text)]
@@ -207,6 +222,7 @@
 defaultParseCommandLine :: ParseCommandLine
 defaultParseCommandLine = return . mapMaybe go
   where
+    {-# INLINE go #-}
     go ('-':'-':as) = case break (=='=') as of
       (a,'=':b) -> Just (pack a, pack b)
       _         -> Nothing
@@ -217,6 +233,7 @@
 loadCommandLine pcl = loadList False "commandLine" (getArgs >>= pcl)
 
 -- | Try load file, if file does not exist then do nothing.
+{-# INLINE tryLoadFile #-}
 tryLoadFile :: MonadIO m => (FilePath -> LoadSalakT m ()) -> FilePath -> LoadSalakT m ()
 tryLoadFile f file = do
   b <- liftIO $ doesFileExist file
diff --git a/src/Salak/Internal/Key.hs b/src/Salak/Internal/Key.hs
--- a/src/Salak/Internal/Key.hs
+++ b/src/Salak/Internal/Key.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE CPP               #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE NoOverloadedLists #-}
 {-# LANGUAGE OverloadedStrings #-}
 module Salak.Internal.Key(
     Key(..)
@@ -12,27 +13,33 @@
   , ToKeys(..)
   , isNum
   , isStr
-  , exprs
+  , keyExpr
+  , Parser
   ) where
 
-import           Control.Applicative  ((<|>))
-import           Data.Attoparsec.Text
-import qualified Data.DList           as D
+import qualified Data.DList                 as D
 import           Data.Hashable
-import           Data.List            (intercalate)
+import           Data.List                  (intercalate)
 import           Data.String
-import           Data.Text            (Text)
-import qualified Data.Text            as T
+import           Data.Text                  (Text)
+import qualified Data.Text                  as T
+import           Data.Void
+import           Text.Megaparsec
+import           Text.Megaparsec.Char
+import           Text.Megaparsec.Char.Lexer
 #if __GLASGOW_HASKELL__ < 804
-import           Data.Semigroup
+import           Data.Semigroup             hiding (option)
 #endif
 
+type Parser = Parsec Void Text
+
 data Key
   = KT !Text
   | KI !Int
   deriving Eq
 
 instance Ord Key where
+  {-# INLINE compare #-}
   compare (KT a) (KT b) = compare a b
   compare (KI a) (KI b) = compare a b
   compare (KI _) _      = LT
@@ -40,28 +47,37 @@
 
 newtype Keys = Keys { unKeys :: D.DList Key } deriving (Eq, Ord)
 
+{-# INLINE emptyKey #-}
 emptyKey :: Keys
 emptyKey = Keys D.empty
 
+{-# INLINE singletonKey #-}
 singletonKey :: Key -> Keys
 singletonKey k = fromKeys [k]
 
+{-# INLINE fromKeys #-}
 fromKeys :: [Key] -> Keys
 fromKeys = Keys . D.fromList
 
+{-# INLINE toKeyList #-}
 toKeyList :: Keys -> [Key]
 toKeyList = D.toList . unKeys
 
 instance Semigroup Keys where
+  {-# INLINE (<>) #-}
   (Keys a) <> (Keys b) = Keys $ a <> b
 
 instance Monoid Keys where
+  {-# INLINE mempty #-}
   mempty = emptyKey
+  {-# INLINE mappend #-}
   mappend = (<>)
 
 instance Show Keys where
+  {-# INLINE show #-}
   show = intercalate "." . go . toKeyList
     where
+      {-# INLINE go #-}
       go (a@(KT _):cs) = let (b,c) = break isStr cs in (show a ++ concat (show <$> b)) : go c
       go (a:cs)          = show a : go cs
       go []              = []
@@ -75,60 +91,64 @@
 isNum _      = False
 
 instance Hashable Key where
+  {-# INLINE hash #-}
   hash (KT a) = hash a
   hash (KI a) = hash a
+  {-# INLINE hashWithSalt #-}
   hashWithSalt i (KT a) = hashWithSalt i a
   hashWithSalt i (KI a) = hashWithSalt i a
 
 instance Show Key where
+  {-# INLINE show #-}
   show (KT x) = T.unpack x
   show (KI i) = "[" ++ show i ++ "]"
 
 simpleKeys :: Text -> Keys
 simpleKeys = fromKeys . fmap KT . filter (not.T.null) . T.splitOn "."
 
-exprs :: Parser [Key]
-exprs = concat <$> ( (expr <|> return []) `sepBy` char '.')
+keyExpr :: Parser [Key]
+keyExpr = concat <$> (option [] expr `sepBy` char '.')
+  where
+    -- xx
+    -- xx.xx
+    -- xx.xx[0]
+    -- xx.xx[1].xx
+    {-# INLINE expr #-}
+    expr :: Parser [Key]
+    expr = (:) <$> sName <*> many sNum
 
-sName :: Parser Key
-sName = KT . T.pack <$> do
-    a <- choice [letter, digit]
-    b <- many' (choice [letter, digit, char '-',  char '_'])
-    return (a:b)
+    {-# INLINE sName #-}
+    sName :: Parser Key
+    sName = KT . T.pack <$> some (alphaNumChar <|> char '-' <|> char '_')
 
-sNum :: Parser Key
-sNum = KI <$> paren decimal
-  where
-    paren e = do
+    {-# INLINE sNum #-}
+    sNum :: Parser Key
+    sNum = do
       _  <- char '['
-      ex <- e
+      ex <- decimal
       _  <- char ']'
-      return ex
-
--- xx
--- xx.xx
--- xx.xx[0]
--- xx.xx[1].xx
-expr :: Parser [Key]
-expr = (:) <$> sName <*> many' sNum
+      return $ KI ex
 
 class ToKeys a where
   toKeys :: a -> Either String Keys
 
 instance IsString Keys where
+  {-# INLINE fromString #-}
   fromString key = case toKeys key of
     Left  _ -> singletonKey (KT $ T.pack key)
     Right k -> k
 
 instance ToKeys Keys where
+  {-# INLINE toKeys #-}
   toKeys = Right
 
 instance ToKeys Text where
-  toKeys = fmap fromKeys . selectors
-    where
-      selectors = go . parse exprs . flip T.snoc '\n'
-      go (Done i r) = if i /= "\n" then Left $ "uncomplete parse" ++ T.unpack i else Right r
-      go a          = Left (show a)
+  -- toKeys = Right . simpleKeys
+  {-# INLINE toKeys #-}
+  toKeys k = case fmap fromKeys (parse keyExpr "" k) of
+    Left  e -> Left (errorBundlePretty e)
+    Right x -> Right x
 
 instance ToKeys String where
+  {-# INLINE toKeys #-}
   toKeys = toKeys . T.pack
diff --git a/src/Salak/Internal/Prop.hs b/src/Salak/Internal/Prop.hs
--- a/src/Salak/Internal/Prop.hs
+++ b/src/Salak/Internal/Prop.hs
@@ -12,6 +12,7 @@
 {-# LANGUAGE UndecidableInstances       #-}
 module Salak.Internal.Prop where
 
+import           Control.Applicative     ((<|>))
 import qualified Control.Applicative     as A
 import           Control.Concurrent.MVar
 import           Control.Monad
@@ -62,17 +63,17 @@
   askReload :: m (IO ReloadResult)
   askReload = reload <$> askSourcePack
 
+  {-# INLINE setLogF #-}
   setLogF :: MonadIO m => (String -> IO ()) -> m ()
   setLogF f = do
     SourcePack{..} <- askSourcePack
     liftIO $ void $ swapMVar lref f
 
+  {-# INLINE logSalak #-}
   logSalak :: MonadIO m => String -> m ()
   logSalak msg = do
     SourcePack{..} <- askSourcePack
-    liftIO $ do
-      f <- readMVar lref
-      f msg
+    liftIO $ readMVar lref >>= ($ msg)
 
   -- | Parse properties using `FromProp`. For example:
   --
@@ -84,16 +85,22 @@
   -- `require` supports parse `IO` values, which actually wrap a 'MVar' variable and can be reseted by reloading configurations.
   -- Normal value will not be affected by reloading configurations.
   require :: (MonadThrow m, FromProp m a) => Text -> m a
-  require ks = askSourcePack >>= \s -> runProp s $ do
-    case toKeys ks of
+  require ks = do
+    s <- askSourcePack
+    runProp s $ case toKeys ks of
       Left  e -> failKey (unpack ks) (PropException e)
       Right k -> withKeys k fromProp
 
+instance Monad m => MonadSalak (ReaderT SourcePack m) where
+  {-# INLINE askSourcePack #-}
+  askSourcePack = ask
+
 -- | Property parser, used to parse property from `Value`
 newtype Prop m a
   = Prop { unProp :: ReaderT SourcePack (ExceptT SomeException m) a }
   deriving (Functor, Applicative, Monad, MonadReader SourcePack, MonadIO)
 
+{-# INLINE runProp #-}
 runProp :: MonadThrow m => SourcePack -> Prop m a -> m a
 runProp sp (Prop p) = do
   v <- runExceptT (runReaderT p sp)
@@ -101,12 +108,15 @@
     Left  e -> throwM e
     Right x -> return x
 
+{-# INLINE withProp #-}
 withProp :: (SourcePack -> SourcePack) -> Prop m a -> Prop m a
 withProp = unsafeCoerce withReaderT
 
+{-# INLINE withKey #-}
 withKey :: Key -> Prop m a -> Prop m a
 withKey = withKeys . singletonKey
 
+{-# INLINE withKeys #-}
 withKeys :: Keys -> Prop m a -> Prop m a
 withKeys key = withProp
   $ \SourcePack{..} ->
@@ -120,7 +130,7 @@
 
 instance Exception SalakException
 
-
+{-# INLINE failKey #-}
 failKey :: Monad m => String -> SalakException -> Prop m a
 failKey ks e = do
   SourcePack{..} <- ask
@@ -128,24 +138,28 @@
     $ SalakException (go (show pref) ks)
     $ toException e
   where
+    {-# INLINE go #-}
     go "" a = a
     go a "" = a
     go a  b = a <> "." <> b
 
 -- | Automatic convert literal string into an instance of `Prop` @m@ @a@.
 instance (Monad m, FromProp m a) => IsString (Prop m a) where
-  fromString ks = do
-    case toKeys ks of
-      Left  e -> failKey ks (PropException e)
-      Right k -> withKeys k fromProp
+  {-# INLINE fromString #-}
+  fromString ks = case toKeys ks of
+    Left  e -> failKey ks (PropException e)
+    Right k -> withKeys k fromProp
 
 
 instance MonadTrans Prop where
+  {-# INLINE lift #-}
   lift = Prop . lift . lift
 
 instance Monad m => A.Alternative (Prop m) where
+  {-# INLINE empty #-}
   empty = failKey "" NullException
 
+  {-# INLINE (<|>) #-}
   a <|> b = do
     v <- try a
     case v of
@@ -153,18 +167,23 @@
       Left (_ :: SomeException) -> b
 
 instance Monad m => MonadError SomeException (Prop m) where
+  {-# INLINE throwError #-}
   throwError = Prop . lift . throwError . toException
+  {-# INLINE catchError #-}
   catchError (Prop ma) me = Prop $ do
     c <- ask
     lift $ catchError (runReaderT ma c) (\e -> runReaderT (unProp $ me e) c)
 
 instance Monad m => MonadThrow (Prop m) where
+  {-# INLINE throwM #-}
   throwM = throwError . toException
 
 instance Monad m => MonadCatch (Prop m) where
+  {-# INLINE catch #-}
   catch ma me = catchError ma (\e -> maybe (throwM e) me $ fromException e)
 
 instance Monad m => MonadFail (Prop m) where
+  {-# INLINE fail #-}
   fail = failKey "" . PropException
 
 -- | Type class used to parse properties.
@@ -173,9 +192,11 @@
   -- | Parse properties from `Value`.
   fromProp :: Monad m => Prop m a
   default fromProp :: (Generic a, GFromProp m (Rep a), Monad m) => Prop m a
+  {-# INLINE fromProp #-}
   fromProp = fmap to gFromProp
 
 instance FromProp m a => FromProp m (Maybe a) where
+  {-# INLINE fromProp #-}
   fromProp = do
     v <- try fromProp
     case v of
@@ -188,6 +209,7 @@
       Right a -> return (Just a)
 
 instance FromProp m a => FromProp m (Either String a) where
+  {-# INLINE fromProp #-}
   fromProp = do
     v <- try fromProp
     return $ case v of
@@ -195,30 +217,36 @@
       Right a -> Right a
 
 instance {-# OVERLAPPABLE #-} FromProp m a => FromProp m [a] where
+  {-# INLINE fromProp #-}
   fromProp = do
     SourcePack{..} <- ask
-    sequence $ (`withKey` fromProp) <$> sort (filter isNum $ HM.keys $ TR.getMap source)
+    sequence $ (`withKey` fromProp) <$> sort (filter isNum $ HM.keys $ TR.tmap source)
 
 instance {-# OVERLAPPABLE #-} (IsString s, FromProp m a) => FromProp m [(s, a)] where
+  {-# INLINE fromProp #-}
   fromProp = do
     SourcePack{..} <- ask
-    sequence $ go <$> sort (filter isStr $ HM.keys $ TR.getMap source)
+    sequence $ go <$> sort (filter isStr $ HM.keys $ TR.tmap source)
     where
       go k = (fromString $ show $ singletonKey k,) <$> withKey k fromProp
 
 instance (Eq s, Hashable s, IsString s, FromProp m a) => FromProp m (HM.HashMap s a) where
+  {-# INLINE fromProp #-}
   fromProp = HM.fromList <$> fromProp
 
 instance (Eq s, Ord s, IsString s, FromProp m a) => FromProp m (M.Map s a) where
+  {-# INLINE fromProp #-}
   fromProp = M.fromList <$> fromProp
 
 -- | Supports for parsing `IO` value.
 instance {-# OVERLAPPABLE #-} (MonadIO m, FromProp (Either SomeException) a, FromProp m a) => FromProp m (IO a) where
+  {-# INLINE fromProp #-}
   fromProp = do
     sp <- ask
     a  <- fromProp
     buildIO sp a
 
+{-# INLINE buildIO #-}
 buildIO :: (MonadIO m, FromProp (Either SomeException) a) => SourcePack -> a -> m (IO a)
 buildIO sp a = liftIO $ do
   aref <- newMVar a
@@ -259,16 +287,19 @@
   -- >   fromProp = Config
   -- >     <$> "enabled" .?: enabled
   -- >     <$> "level"   .?: level
+  {-# INLINE (.?:) #-}
   infixl 5 .?:
   (.?:) :: Default b => f a -> (b -> a) -> f a
   (.?:) fa b = fa .?= b def
 
 -- | Support for setting default normal value.
 instance {-# OVERLAPPABLE #-} A.Alternative f => PropOp f a where
+  {-# INLINE (.?=) #-}
   (.?=) a b = a A.<|> pure b
 
 -- | Support for setting default `IO` value.
 instance (MonadIO m, FromProp (Either SomeException) a) => PropOp (Prop m) (IO a) where
+  {-# INLINE (.?=) #-}
   (.?=) ma a = do
     sp <- ask
     v  <- try ma
@@ -277,13 +308,16 @@
       Right o                    -> return o
 
 instance Monad m => HasValid (Prop m) where
+  {-# INLINE invalid #-}
   invalid = Control.Monad.Fail.fail . toI18n
 
--- | Parse primitive value from `Value`
-readPrimitive :: Monad m => (Value -> Either String a) -> Prop m a
-readPrimitive f = do
+{-# INLINE readPrimitive' #-}
+readPrimitive' :: Monad m => (Value -> Either String a) -> Prop m (Maybe a)
+readPrimitive' f = do
   SourcePack{..} <- ask
-  let go (VRT t   : as) = if null as then return t else (t <>) <$> go as
+  let {-# INLINE go #-}
+      go [VRT t]        = return t
+      go (VRT t   : as) = (t <>) <$> go as
       go (VRR k d : as) = if S.member k kref
         then Control.Monad.Fail.fail $ "reference cycle of key " <> show k
         else do
@@ -295,22 +329,30 @@
           w <- case t of
             Just x -> return x
             _      -> if null d then A.empty else go d
-          if null as then return w else (w <>) <$> go as
+          go (VRT w : as)
       go [] = A.empty
+      {-# INLINE g2 #-}
       g2 (VR r) = VT <$> go r
       g2 v      = return v
-  case TR.getPrimitive source >>= getVal of
-    Just v      -> do
-      vy <- g2 v
-      case f vy of
-        Left  e -> Control.Monad.Fail.fail e
-        Right a -> return a
-    _           -> A.empty
+      {-# INLINE g3 #-}
+      g3 vy
+        | nullValue vy = return Nothing
+        | otherwise    = case f vy of
+            Left  e -> Control.Monad.Fail.fail e
+            Right a -> return (Just a)
+  maybe A.empty (g2 >=> g3) $ TR.tvar source >>= getVal
 
+-- | Parse primitive value from `Value`
+{-# INLINE readPrimitive #-}
+readPrimitive :: Monad m => (Value -> Either String a) -> Prop m a
+readPrimitive f = readPrimitive' f >>= maybe A.empty return
+
 -- | Parse enum value from `Text`
+{-# INLINE readEnum #-}
 readEnum :: Monad m => (Text -> Either String a) -> Prop m a
 readEnum = readPrimitive . go
   where
+    {-# INLINE go #-}
     go f (VT t) = f t
     go _ x      = Left $ fst (typeOfV x) ++ " cannot convert to enum"
 
@@ -319,89 +361,116 @@
   gFromProp :: Monad m => Prop m (f a)
 
 instance {-# OVERLAPPABLE #-} (Constructor c, GFromProp m a) => GFromProp m (M1 C c a) where
-    gFromProp
-      | conIsRecord m = fmap M1 gFromProp
-      | otherwise     = fmap M1 $ gEnum $ T.pack (conName m)
-      where m = undefined :: t c a x
+  {-# INLINE gFromProp #-}
+  gFromProp
+    | conIsRecord m = fmap M1 gFromProp
+    | otherwise     = fmap M1 $ gEnum $ T.pack (conName m)
+    where m = undefined :: t c a x
 
+{-# INLINE gEnum #-}
 gEnum :: (GFromProp m f, Monad m) => Text -> Prop m (f a)
 gEnum va = do
   o <- gFromProp
   readEnum $ \x -> if x==va then Right o else Left "enum invalid"
 
 instance {-# OVERLAPPABLE #-} (Selector s, GFromProp m a) => GFromProp m(M1 S s a) where
+  {-# INLINE gFromProp #-}
   gFromProp = withKey (KT $ T.pack $ selName (undefined :: t s a p)) $ M1 <$> gFromProp
 
 instance {-# OVERLAPPABLE #-} GFromProp m a => GFromProp m (M1 D i a) where
+  {-# INLINE gFromProp #-}
   gFromProp = M1 <$> gFromProp
 
 instance {-# OVERLAPPABLE #-} FromProp m a => GFromProp m (K1 i a) where
-    gFromProp = fmap K1 fromProp
+  {-# INLINE gFromProp #-}
+  gFromProp = fmap K1 fromProp
 
 instance Monad m => GFromProp m U1 where
+  {-# INLINE gFromProp #-}
   gFromProp = pure U1
 
 instance {-# OVERLAPPABLE #-} (GFromProp m a, GFromProp m b) => GFromProp m (a:*:b) where
+  {-# INLINE gFromProp #-}
   gFromProp = (:*:) <$> gFromProp <*> gFromProp
 
 instance {-# OVERLAPPABLE #-} (GFromProp m a, GFromProp m b) => GFromProp m (a:+:b) where
+  {-# INLINE gFromProp #-}
   gFromProp = fmap L1 gFromProp A.<|> fmap R1 gFromProp
 
 instance FromProp m a => FromProp m (Identity a) where
+  {-# INLINE fromProp #-}
   fromProp = Identity <$> fromProp
 
 instance (FromProp m a, FromProp m b) => FromProp m (a,b) where
+  {-# INLINE fromProp #-}
   fromProp = (,) <$> fromProp <*> fromProp
 
 instance (FromProp m a, FromProp m b, FromProp m c) => FromProp m(a,b,c) where
+  {-# INLINE fromProp #-}
   fromProp = (,,) <$> fromProp <*> fromProp <*> fromProp
 
 instance (FromProp m a, FromProp m b, FromProp m c, FromProp m d) => FromProp m(a,b,c,d) where
+  {-# INLINE fromProp #-}
   fromProp = (,,,) <$> fromProp <*> fromProp <*> fromProp <*> fromProp
 
 instance (FromProp m a, FromProp m b, FromProp m c, FromProp m d, FromProp m e) => FromProp m(a,b,c,d,e) where
+  {-# INLINE fromProp #-}
   fromProp = (,,,,) <$> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp
 
 instance (FromProp m a, FromProp m b, FromProp m c, FromProp m d, FromProp m e, FromProp m f) => FromProp m(a,b,c,d,e,f) where
+  {-# INLINE fromProp #-}
   fromProp = (,,,,,) <$> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp
 
 instance (FromProp m a, FromProp m b, FromProp m c, FromProp m d, FromProp m e, FromProp m f, FromProp m g) => FromProp m(a,b,c,d,e,f,g) where
+  {-# INLINE fromProp #-}
   fromProp = (,,,,,,) <$> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp
 
 instance (FromProp m a, FromProp m b, FromProp m c, FromProp m d, FromProp m e, FromProp m f, FromProp m g, FromProp m h) => FromProp m(a,b,c,d,e,f,g,h) where
+  {-# INLINE fromProp #-}
   fromProp = (,,,,,,,) <$> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp
 
 instance (FromProp m a, FromProp m b, FromProp m c, FromProp m d, FromProp m e, FromProp m f, FromProp m g, FromProp m h, FromProp m i) => FromProp m(a,b,c,d,e,f,g,h,i) where
+  {-# INLINE fromProp #-}
   fromProp = (,,,,,,,,) <$> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp <*> fromProp
 
 
 instance FromProp m a => FromProp m (Min a) where
+  {-# INLINE fromProp #-}
   fromProp = Min <$> fromProp
 
 instance FromProp m a => FromProp m (Max a) where
+  {-# INLINE fromProp #-}
   fromProp = Max <$> fromProp
 
 instance FromProp m a => FromProp m (First a) where
+  {-# INLINE fromProp #-}
   fromProp = First <$> fromProp
 
 instance FromProp m a => FromProp m (Last a) where
+  {-# INLINE fromProp #-}
   fromProp = Last <$> fromProp
 
 instance FromProp m a => FromProp m (Dual a) where
+  {-# INLINE fromProp #-}
   fromProp = Dual <$> fromProp
 
 instance FromProp m a => FromProp m (Sum a) where
+  {-# INLINE fromProp #-}
   fromProp = Sum <$> fromProp
 
 instance FromProp m a => FromProp m (Product a) where
+  {-# INLINE fromProp #-}
   fromProp = Product <$> fromProp
 
 instance FromProp m a => FromProp m (Option a) where
+  {-# INLINE fromProp #-}
   fromProp = Option <$> fromProp
 
 instance FromProp m Bool where
+  {-# INLINE fromProp #-}
   fromProp = readPrimitive go
     where
+      {-# INLINE go #-}
       go (VB x) = Right x
       go (VT x) = case T.toLower x of
         "true"  -> Right True
@@ -412,26 +481,34 @@
       go x      = Left $ getType x ++ " cannot be bool"
 
 instance FromProp m Text where
-  fromProp = readPrimitive go
+  {-# INLINE fromProp #-}
+  fromProp = fromMaybe "" <$> readPrimitive' go
     where
+      {-# INLINE go #-}
       go (VT x) = Right x
       go x      = Right $ T.pack $ snd $ typeOfV x
 
 instance FromProp m TL.Text where
+  {-# INLINE fromProp #-}
   fromProp = TL.fromStrict <$> fromProp
 
 instance FromProp m B.ByteString where
+  {-# INLINE fromProp #-}
   fromProp = TB.encodeUtf8 <$> fromProp
 
 instance FromProp m BL.ByteString where
+  {-# INLINE fromProp #-}
   fromProp = TBL.encodeUtf8 <$> fromProp
 
 instance FromProp m String where
+  {-# INLINE fromProp #-}
   fromProp = T.unpack <$> fromProp
 
 instance FromProp m Scientific where
+  {-# INLINE fromProp #-}
   fromProp = readPrimitive go
     where
+      {-# INLINE go #-}
       go (VT x) = case readMaybe $ T.unpack x of
         Just v -> Right v
         _      -> Left  "string convert number failed"
@@ -439,90 +516,118 @@
       go x      = Left $ getType x ++ " cannot be number"
 
 instance FromProp m Float where
+  {-# INLINE fromProp #-}
   fromProp = toRealFloat <$> fromProp
 
 instance FromProp m Double where
+  {-# INLINE fromProp #-}
   fromProp = toRealFloat <$> fromProp
 
 instance FromProp m Integer where
+  {-# INLINE fromProp #-}
   fromProp = toInteger <$> (fromProp :: Prop m Int)
 
 instance FromProp m Int where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m Int8 where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m Int16 where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m Int32 where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m Int64 where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m Word where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m Word8 where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m Word16 where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m Word32 where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m Word64 where
+  {-# INLINE fromProp #-}
   fromProp = fromProp >>= toNum
 
 instance FromProp m NominalDiffTime where
+  {-# INLINE fromProp #-}
   fromProp = fromInteger <$> fromProp
 
 instance FromProp m DiffTime where
+  {-# INLINE fromProp #-}
   fromProp = fromInteger <$> fromProp
 
 instance (HasResolution a, Monad m) => FromProp m (Fixed a) where
+  {-# INLINE fromProp #-}
   fromProp = fromInteger <$> fromProp
 
+{-# INLINE toNum #-}
 toNum :: (Monad m, Integral i, Bounded i) => Scientific -> Prop m i
 toNum s = case toBoundedInteger s of
   Just v -> return v
   _      -> Control.Monad.Fail.fail "scientific number doesn't fit in the target representation"
 
 instance FromProp m CBool where
+  {-# INLINE fromProp #-}
   fromProp = do
     b <- fromProp
     return $ if b then 1 else 0
 
 instance FromProp m CShort where
+  {-# INLINE fromProp #-}
   fromProp = CShort <$> fromProp
 
 instance FromProp m CUShort where
+  {-# INLINE fromProp #-}
   fromProp = CUShort <$> fromProp
 
 instance FromProp m CInt where
+  {-# INLINE fromProp #-}
   fromProp = CInt <$> fromProp
 
 instance FromProp m CUInt where
+  {-# INLINE fromProp #-}
   fromProp = CUInt <$> fromProp
 
 instance FromProp m CLong where
+  {-# INLINE fromProp #-}
   fromProp = CLong <$> fromProp
 
 instance FromProp m CULong where
+  {-# INLINE fromProp #-}
   fromProp = CULong <$> fromProp
 
 instance FromProp m CLLong where
+  {-# INLINE fromProp #-}
   fromProp = CLLong <$> fromProp
 
 instance FromProp m CULLong where
+  {-# INLINE fromProp #-}
   fromProp = CULLong <$> fromProp
 
 instance FromProp m CFloat where
+  {-# INLINE fromProp #-}
   fromProp = CFloat <$> fromProp
 
 instance FromProp m CDouble where
+  {-# INLINE fromProp #-}
   fromProp = CDouble <$> fromProp
 
diff --git a/src/Salak/Internal/Source.hs b/src/Salak/Internal/Source.hs
--- a/src/Salak/Internal/Source.hs
+++ b/src/Salak/Internal/Source.hs
@@ -12,7 +12,8 @@
 import qualified Salak.Trie              as T
 
 type Source = T.Trie Vals
-type TraceSource = T.Trie ([String], Vals)
+type TraceVals = ([String], Vals)
+type TraceSource = T.Trie TraceVals
 
 -- | Reload result, show erros or changes.
 data ReloadResult = ReloadResult
@@ -37,6 +38,7 @@
 diff :: Source -> Source -> T.Trie ModType
 diff = T.unionWith' go
   where
+    {-# INLINE go #-}
     go Nothing Nothing = Nothing
     go (Just a) Nothing  = if nullVals a then Nothing else Just Add
     go Nothing (Just a)  = if nullVals a then Nothing else Just Del
@@ -61,27 +63,23 @@
 gen :: (Foldable f, ToKeys k, ToValue v) => Int -> f (k,v) -> TraceSource
 gen i = foldr go T.empty
   where
+    {-# INLINE go #-}
     go (k,v) x = case toKeys k of
-      Left  e  -> T.alter (g3 e) mempty x
-      Right k' -> T.alter (g2 $ Val i $ toVal v) k' x
-    g2 v (Just (a,c)) = case modVals v c of
-      Left  e -> Just (e:a, c)
-      Right d -> Just (a,   d)
-    g2 v _            = case singletonVals v of
-      Left  e -> Just ([e], emptyVals)
-      Right d -> Just ([], d)
-    g3 e (Just (a,c)) = Just (e:a,c)
-    g3 e _            = Just ([e], emptyVals)
+      Left  e  -> T.alter (setErr0 e) mempty x
+      Right k' -> T.alter (setVal0 $ Val i $ toVal v) k' x
 
+{-# INLINE fmt #-}
 fmt :: ModType -> Int -> String -> String -> String
 fmt m i s n = concat ['#' : show i, ' ' : show m, ' ' : s ,  ' ' : n]
 
+{-# INLINE fmtMod #-}
 fmtMod :: Int -> String -> HashMap String ModType -> [String]
 fmtMod i name cs = fmap (\(k,v)-> fmt v i k name) (HM.toList cs)
 
 loadSource :: (Int -> IO TraceSource) -> Int -> TraceSource -> IO TraceSource
 loadSource f i ts = T.unionWith go ts <$> f i
   where
+    {-# INLINE go #-}
     go Nothing Nothing               = Nothing
     go (Just v) Nothing              = Just v
     go Nothing (Just v)              = Just v
@@ -89,12 +87,29 @@
       Left  e -> Just (e:e1++e2, v2)
       Right v -> Just (e1++e2,v)
 
+{-# INLINE traceError #-}
+traceError :: Maybe TraceVals -> [String]
+traceError Nothing      = []
+traceError (Just (e,_)) = e
+
+{-# INLINE traceVals #-}
+traceVals :: Maybe TraceVals -> Vals
+traceVals Nothing      = emptyVals
+traceVals (Just (_,v)) = v
+
+{-# INLINE setErr0 #-}
+setErr0 :: String -> Maybe TraceVals -> Maybe TraceVals
+setErr0 e (Just (a,c)) = Just (e:a,c)
+setErr0 e _            = Just ([e], emptyVals)
+
+{-# INLINE setVal0 #-}
+setVal0 :: Val Value -> Maybe TraceVals -> Maybe TraceVals
+setVal0 v tv =
+  let tv2 = traceVals tv
+  in case modVals v tv2 of
+      Left  e -> Just (e : traceError tv, tv2)
+      Right x -> Just (traceError tv, x)
+
+{-# INLINE setVal #-}
 setVal :: ToValue v => Int -> v -> TraceSource -> TraceSource
-setVal i v = T.update go
-  where
-    go Nothing       = case modVals (Val i $ toVal v) emptyVals of
-      Left  e -> Just ([e], emptyVals)
-      Right x -> Just ([], x)
-    go (Just (e,vs)) = case modVals (Val i $ toVal v) vs of
-      Left  e1 -> Just (e1:e, vs)
-      Right x  -> Just (e, x)
+setVal i v = T.update (setVal0 $ Val i $ toVal v)
diff --git a/src/Salak/Internal/Val.hs b/src/Salak/Internal/Val.hs
--- a/src/Salak/Internal/Val.hs
+++ b/src/Salak/Internal/Val.hs
@@ -1,12 +1,10 @@
 {-# LANGUAGE CPP               #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE NoOverloadedLists #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Salak.Internal.Val where
 
-import           Control.Applicative
-import           Data.Attoparsec.Text
+import           Control.Applicative  ((<|>))
 import           Data.ByteString      (ByteString)
 import           Data.Heap            (Heap)
 import qualified Data.Heap            as H
@@ -18,8 +16,10 @@
 import           Data.Text.Encoding   (decodeUtf8)
 import           Data.Time
 import           Salak.Internal.Key
+import           Text.Megaparsec
+import           Text.Megaparsec.Char
 #if __GLASGOW_HASKELL__ < 804
-import           Data.Semigroup((<>))
+import           Data.Semigroup       ((<>))
 #endif
 
 
@@ -33,6 +33,7 @@
 
 type Priority = Int
 
+{-# INLINE priority #-}
 priority :: Val v -> Int
 priority (Val i _) = i
 
@@ -45,8 +46,8 @@
   show (VRT t)  = T.unpack t
   show (VRR k m) = "${" <> show k <> (if null m then go m else ":" <> go m) <> "}"
     where
-      go []     = ""
-      go (x:as) = show x <> go as
+      {-# INLINE go #-}
+      go = foldr ((<>) . show) ""
 
 data Value
   = VT  !Text
@@ -55,67 +56,60 @@
   | VLT !LocalTime
   | VD  !Day
   | VH  !TimeOfDay
-  | VZT !TimeZone !LocalTime
   | VU  !UTCTime
   | VR  ![VRef]
   deriving Eq
 
+{-# INLINE nullValue #-}
+nullValue :: Value -> Bool
+nullValue (VT x)  = T.null x
+nullValue (VR []) = True
+nullValue _       = False
+
 instance Show Value where
   show v = let (a,b) = typeOfV v in b ++ "::" ++ a
 
+{-# INLINE typeOfV #-}
 typeOfV :: Value -> (String, String)
-typeOfV (VT  b)   = ("Str",       show b)
-typeOfV (VI  b)   = ("Num",       show b)
-typeOfV (VB  b)   = ("Bool",      show b)
-typeOfV (VLT b)   = ("LocalTime", show b)
-typeOfV (VD  b)   = ("Day",       show b)
-typeOfV (VH  b)   = ("TimeOfDay", show b)
-typeOfV (VZT _ b) = ("ZonedTime", show b)
-typeOfV (VU  b)   = ("UTCTime",   show b)
-typeOfV (VR  b)   = ("Ref",       show b)
+typeOfV (VT  b) = ("Str",       show b)
+typeOfV (VI  b) = ("Num",       show b)
+typeOfV (VB  b) = ("Bool",      show b)
+typeOfV (VLT b) = ("LocalTime", show b)
+typeOfV (VD  b) = ("Day",       show b)
+typeOfV (VH  b) = ("TimeOfDay", show b)
+typeOfV (VU  b) = ("UTCTime",   show b)
+typeOfV (VR  b) = ("Ref",       show b)
 
+{-# INLINE getType #-}
 getType :: Value -> String
 getType = fst . typeOfV
 
+{-# INLINE mkValue #-}
 mkValue :: Value -> Either String Value
-mkValue (VT v) = case parseOnly (vref <* endOfInput) v of
-  Left  e       -> Left e
-  Right x       -> Right $ case go x of
-    [VRT _] -> VT v
-    vs      -> VR vs
-  where
-    go (VRT a:VRT b:as) = go (VRT (a <> b):as)
-    go (VRT a:b:as)     = VRT a:b:go as
-    go (a:as)           = a : go as
-    go []               = []
+mkValue (VT v) = if T.null v
+  then Right (VT v)
+  else case parse vref "" v of
+    Left  e   -> Left (errorBundlePretty e)
+    Right y   -> Right $ case y of
+      [VRT x] -> VT x
+      vs      -> VR vs
 mkValue v      = Right v
 
--- mkValue' :: Value -> Value
--- mkValue' v = case mkValue v of
---   Left  _ -> v
---   Right x -> x
-
+{-# INLINE exprChar #-}
 exprChar :: Parser Char
-exprChar = satisfy (notInClass "\\${}") <|> go
+exprChar = noneOf go <|> (char '\\' >> oneOf go)
   where
-    go = do
-      a <- char '\\'
-      v <- peekChar
-      case v of
-        Just '\\' -> char '\\'
-        Just '$'  -> char '$'
-        Just '{'  -> char '{'
-        Just '}'  -> char '}'
-        Just x    -> fail $ "error char sequence \\" <> [x]
-        _         -> return a
+    go :: [Token Text]
+    go = "${}\\"
 
 vref :: Parser [VRef]
-vref = many1' (go <|> (VRT . T.pack <$> many1' exprChar))
+vref = some (go <|> (VRT . T.pack <$> some exprChar))
   where
+    {-# INLINE go #-}
     go = do
       _ <- string "${"
-      k <- exprs
-      v <- option [] $ (char ':' >> option [VRT ""] vref )
+      k <- keyExpr
+      v <- option [] $ char ':' >> option [VRT ""] vref
       _ <- char '}'
       return (VRR (fromKeys k) v)
 
@@ -129,20 +123,25 @@
 instance Eq v => Ord (Val v) where
   compare (Val a _) (Val b _) = compare a b
 
+{-# INLINE nullVals #-}
 nullVals :: Vals -> Bool
 nullVals (Vals v) = H.null v
 
+{-# INLINE minimumVals #-}
 minimumVals :: Vals -> Val Value
 minimumVals (Vals h) = H.minimum h
 
+{-# INLINE emptyVals #-}
 emptyVals :: Vals
 emptyVals = Vals H.empty
 
+{-# INLINE deleteVals #-}
 deleteVals :: Int -> Vals -> (Bool, Vals)
 deleteVals i (Vals v) =
   let (a,b) = H.partition ((==i) . priority) v
   in (H.null a, Vals b)
 
+{-# INLINE getVal #-}
 getVal :: Vals -> Maybe Value
 getVal (Vals v)
   | H.null v = Nothing
@@ -152,38 +151,50 @@
   toVal :: a -> Value
 
 instance ToValue Value where
+  {-# INLINE toVal #-}
   toVal = id
 
 instance ToValue Text where
+  {-# INLINE toVal #-}
   toVal = VT
 
 instance ToValue ByteString where
+  {-# INLINE toVal #-}
   toVal = VT . decodeUtf8
 
 instance ToValue String where
+  {-# INLINE toVal #-}
   toVal = VT . T.pack
 
 instance ToValue Scientific where
+  {-# INLINE toVal #-}
   toVal = VI
 
 instance ToValue Integer where
+  {-# INLINE toVal #-}
   toVal = VI . fromInteger
 
 instance ToValue Int where
+  {-# INLINE toVal #-}
   toVal = VI . fromInteger . toInteger
 
 instance ToValue Int64 where
+  {-# INLINE toVal #-}
   toVal = VI . fromInteger . toInteger
 
 instance ToValue Double where
+  {-# INLINE toVal #-}
   toVal = VI . realToFrac
 
 instance ToValue Bool where
+  {-# INLINE toVal #-}
   toVal = VB
 
 instance ToValue UTCTime where
+  {-# INLINE toVal #-}
   toVal = VU
 
+{-# INLINE delVals #-}
 delVals :: Int -> Vals -> Vals
 delVals p (Vals v) = Vals $ H.filter ((/=p) . priority) v
 
@@ -192,6 +203,7 @@
   Left  e -> Left e
   Right y -> Right $ Vals $ H.insert (Val p y) $ H.filter ((/=p) . priority) v
 
+{-# INLINE singletonVals #-}
 singletonVals :: Val Value -> Either String Vals
 singletonVals (Val p x) = case mkValue x of
   Left  e -> Left e
diff --git a/src/Salak/Internal/Writable.hs b/src/Salak/Internal/Writable.hs
--- a/src/Salak/Internal/Writable.hs
+++ b/src/Salak/Internal/Writable.hs
@@ -20,12 +20,14 @@
   }
 
 -- | Convert a `IO` value to `Writable` value.
+{-# INLINE toWritable #-}
 toWritable :: IO a -> IO (Writable a)
 toWritable valRef = do
   setRef <- newMVar Nothing
   return Writable{..}
 
 -- | Get value.
+{-# INLINE getWritable #-}
 getWritable :: Writable a -> IO a
 getWritable Writable{..} = do
   v <- readMVar setRef
@@ -34,5 +36,6 @@
     _      -> valRef
 
 -- | Set or remove override value.
+{-# INLINE setWritable #-}
 setWritable :: Maybe a -> Writable a -> IO ()
 setWritable s Writable{..} = void $ swapMVar setRef s
diff --git a/src/Salak/Trie.hs b/src/Salak/Trie.hs
--- a/src/Salak/Trie.hs
+++ b/src/Salak/Trie.hs
@@ -3,7 +3,7 @@
 -- |
 -- Module:      Salak.Internal
 -- Copyright:   2019 Daniel YU
--- License:     BSD3
+-- License:     MIT
 -- Maintainer:  leptonyu@gmail.com
 -- Stability:   experimental
 -- Portability: portable
@@ -11,9 +11,7 @@
 -- A data structure for manipulating properties.
 --
 module Salak.Trie(
-    Trie
-  , getPrimitive
-  , getMap
+    Trie(..)
   -- * Construction
   , empty
   , singleton
@@ -57,115 +55,137 @@
 import           Text.Show           (Show (..))
 
 -- | A Trie from keys `Key` to values @v@, which is a recursive map.
-data Trie v = Trie !(Maybe v) !(HashMap Key (Trie v)) deriving (Eq, Functor)
+data Trie v = Trie
+  { tvar :: !(Maybe v)
+  , tmap :: !(HashMap Key (Trie v))
+  } deriving (Eq, Functor)
 
 instance Show v => Show (Trie v) where
   show t = intercalate "\n" $ map (\(k,v)-> show k ++ ":" ++ show v) $ Salak.Trie.toList t
 
 instance Foldable Trie where
-  foldr f b (Trie v m) = foldr (flip (foldr f)) (go v) m
+  {-# INLINE foldr #-}
+  foldr f b Trie{..} = foldr (flip (foldr f)) (go tvar) tmap
     where
+      {-# INLINE go #-}
       go (Just x) = f x b
       go _        = b
 
 instance Traversable Trie where
-  traverse f (Trie v m) = Trie <$> go v <*> traverse (traverse f) m
+  {-# INLINE traverse #-}
+  traverse f Trie{..} = Trie <$> go tvar <*> traverse (traverse f) tmap
     where
+      {-# INLINE go #-}
       go (Just x) = Just <$> f x
       go _        = pure Nothing
 
 -- | /O(1)/. A trie with a single element.
+{-# INLINE singleton #-}
 singleton :: v -> Trie v
 singleton v = Trie (Just v) HM.empty
 
 -- | /O(1)/. The empty trie.
+{-# INLINE empty #-}
 empty :: Trie v
 empty = Trie Nothing HM.empty
 
--- | Get primitive value of a trie.
-getPrimitive :: Trie v -> Maybe v
-getPrimitive (Trie v _) = v
-
--- | Get map value of a trie.
-getMap :: Trie v -> HashMap Key (Trie v)
-getMap (Trie _ m) = m
-
 -- | /O(1)/. Return True if this trie is empty, False otherwise.
+{-# INLINE null #-}
 null :: Trie v -> Bool
 null (Trie Nothing e) = HM.null e
 null _                = False
 
 -- | /O(log (n+m))/. Return True if the specified key is present in the trie, False otherwise.
+{-# INLINE member #-}
 member :: Eq v => Keys -> Trie v -> Bool
 member k t = isJust (lookup k t)
 
+{-# INLINE subTrie #-}
 subTrie :: Key -> Trie v -> Trie v
-subTrie key = fromMaybe empty . HM.lookup key . getMap
+subTrie key = fromMaybe empty . HM.lookup key . tmap
 
+{-# INLINE subTries #-}
 subTries :: Keys -> Trie v -> Trie v
-subTries ks v = foldl (flip subTrie) v (toKeyList ks)
+subTries = flip (foldl' $ flip subTrie) . toKeyList
 
 -- | /O(log (n+m))/. Return the primitive value to which the specified key is mapped,
 -- or Nothing if this trie contains no mapping for the key.
+{-# INLINE lookup #-}
 lookup :: Eq v => Keys -> Trie v -> Maybe v
-lookup keys = getPrimitive . subTries keys
+lookup keys = tvar . subTries keys
 
 -- | /O(log n)/. Associate the specified value with the specified key in this trie.
 -- If this trie previously contained a mapping for the key, the old value is replaced.
+{-# INLINE insert #-}
 insert :: Eq v => Keys -> v -> Trie v -> Trie v
-insert ks v = alter (\_ -> Just v) ks
+insert ks v = alter (const $ Just v) ks
 
 -- | /O(log m)/. The expression (`modify` k f trie) modifies the sub trie at k.
+{-# INLINE modify #-}
 modify :: Eq v => Key -> (Trie v -> Trie v) -> Trie v -> Trie v
-modify k f (Trie v m) = Trie v $ HM.alter (go . f . fromMaybe empty) k m
-  where
-    go x = if x == empty then Nothing else Just x
+modify k f (Trie v m) = Trie v $ HM.alter (convert . f . fromMaybe empty) k m
 
+{-# INLINE convert #-}
+convert :: Eq v => Trie v -> Maybe (Trie v)
+convert x = if x == empty then Nothing else Just x
+
 -- | /O(log (n+m))/. The expression (`modify'` ks f trie) modifies the sub trie at ks.
-modify' :: Eq v => Keys -> (Trie v -> Trie v) -> Trie v -> Trie v
-modify' ks f = foldr modify f (toKeyList ks)
+{-# INLINE modify' #-}
+modify' :: Eq v => (Trie v -> Trie v) -> Keys -> Trie v -> Trie v
+modify' f = foldr modify f . toKeyList
 
 -- | /O(1)/. The expression (update f trie) updates the primitive value in the trie.
+{-# INLINE update #-}
 update :: Eq v => (Maybe v -> Maybe v) -> Trie v -> Trie v
-update f = alter f mempty
+update = flip alter mempty
 
 -- | /O(n)/. The expression (update f ks trie) updates the primitive value of sub trie at ks.
+{-# INLINE alter #-}
 alter :: Eq v => (Maybe v -> Maybe v) -> Keys -> Trie v -> Trie v
-alter f keys = modify' keys (\(Trie a b) -> Trie (f a) b)
+alter f = modify' (\(Trie a b) -> Trie (f a) b)
 
 -- | /O(n*m)/. Return a list of this tries's elements. The list is produced lazily.
 toList :: Trie v -> [(Keys, v)]
 toList = go D.empty
   where
+    {-# INLINE go #-}
     go p (Trie (Just v) m) = (Keys p, v) : g2 p m
     go p (Trie _        m) = g2 p m
+    {-# INLINE g2 #-}
     g2 p m = concat $ g3 p <$> HM.toList m
+    {-# INLINE g3 #-}
     g3 p (k,t) = go (D.snoc p k) t
 
 -- | /O(n*m*log n)/. Construct a trie with the supplied mappings.
 -- If the list contains duplicate mappings, the later mappings take precedence.
+{-# INLINE fromList #-}
 fromList :: Eq v => [(Keys, v)] -> Trie v
 fromList = foldr (uncurry insert) empty
 
 -- | /O(n)/. Filter this trie by retaining only elements which values satisfy a predicate.
+{-# INLINE filter #-}
 filter :: Eq v => (v -> Bool) -> Trie v -> Trie v
 filter f (Trie v m) = if ok v then Trie v go else Trie Nothing go
   where
+    {-# INLINE ok #-}
     ok (Just x) = f x
     ok _        = False
-    go = HM.mapMaybe (g2 . filter f) m
-    g2 x = if x == empty then Nothing else Just x
+    {-# INLINE go #-}
+    go = HM.mapMaybe (convert . filter f) m
 
 -- | /O(n+m)/. The union of two tries.
 -- If a key occurs in both tries, the provided function (first argument) will be used to compute the result.
+{-# INLINE unionWith #-}
 unionWith :: Eq v => (Maybe v -> Maybe v -> Maybe v) -> Trie v -> Trie v -> Trie v
 unionWith f (Trie v1 m1) (Trie v2 m2) = Trie (f v1 v2) $ HM.unionWith (unionWith f) m1 m2
 
 -- | /O(n+m)/. The union of two tries.
 -- All the keys will be calculated by the provided function.
+{-# INLINE unionWith' #-}
 unionWith' :: (Maybe v -> Maybe v -> Maybe v3) -> Trie v -> Trie v -> Trie v3
 unionWith' f (Trie v1 m1) (Trie v2 m2) = Trie (f v1 v2) $ foldr go HM.empty $ HM.keys $ HM.union m1 m2
   where
+    {-# INLINE go #-}
     go k =
       let x1 = fromMaybe empty $ HM.lookup k m1
           x2 = fromMaybe empty $ HM.lookup k m2
diff --git a/test/Salak/Internal/KeySpec.hs b/test/Salak/Internal/KeySpec.hs
--- a/test/Salak/Internal/KeySpec.hs
+++ b/test/Salak/Internal/KeySpec.hs
@@ -1,4 +1,4 @@
-module Salak.Internal.KeySpec where
+module Salak.Internal.KeySpec(spec) where
 
 import           Data.List          (intercalate)
 import           Data.Text          (Text, pack, unpack)
diff --git a/test/Salak/Internal/PropSpec.hs b/test/Salak/Internal/PropSpec.hs
--- a/test/Salak/Internal/PropSpec.hs
+++ b/test/Salak/Internal/PropSpec.hs
@@ -1,45 +1,113 @@
-module Salak.Internal.PropSpec where
+module Salak.Internal.PropSpec(spec) where
 
-import           Control.Concurrent.MVar
 import           Control.Monad.Writer
 import           Data.Either
 import           Data.Int
-import qualified Data.Set                as S
+import           Data.Scientific
+import           Data.Text            (Text)
+import           Data.Word
 import           Salak
 import           Salak.Internal
+import           Salak.Internal.Prop
 import           Salak.Internal.Val
-import qualified Salak.Trie              as T
 import           Test.Hspec
 
-newSource :: Value -> Source
-newSource v = case modVals (Val 0 v) emptyVals of
-  Left  _ -> T.empty
-  Right x -> T.singleton x
-
-newSourcePack :: Value -> IO SourcePack
-newSourcePack v = do
-  let source = newSource v
-      origin = source
-      kref   = S.empty
-      pref   = mempty
-      reload = return $ ReloadResult False []
-  qref <- newMVar $ \_ -> Right (return ())
-  lref <- newMVar $ \_ -> return ()
-  return SourcePack{..}
-
 spec :: SpecWith ()
 spec = do
-  context "VT" $ do
-    it "text" $ do
-      sp <- newSourcePack (VT "128")
-      let run :: forall m a. Monad m => RunSalakT m a -> m a
-          run = (`runRun` sp)
-      vInt   <- run $ require ""
-      vInt32 <- run $ require ""
-      128 `shouldBe` (vInt   :: Int)
-      128 `shouldBe` (vInt32 :: Int32)
+  context "Prop" $ do
+    let vals =
+          [ ("int",  "128")
+          , ("bool-1", "yes")
+          , ("bool-2", "no")
+          , ("bool-3", "true")
+          , ("bool-4", "false")
+          , ("bool-L1", "YES")
+          , ("bool-L2", "NO")
+          , ("bool-L3", "TRUE")
+          , ("bool-L4", "FALSE")
+          , ("int-", "-1")
+          , ("empty", "")
+          ]
+        run :: ((forall a. FromProp IO a => Text -> IO a) -> RunSalak ()) -> IO ()
+        run g = loadAndRunSalak (loadMock vals) $ do
+          sp <- askSourcePack
+          let f k = case toKeys k of
+                Left  e -> fail e
+                Right x -> runProp sp (withKeys x fromProp)
+          g f
+    it "bool" $ run $ \r -> do
+      b1  <- require "bool-1"
+      b2  <- require "bool-2"
+      b3  <- require "bool-3"
+      b4  <- require "bool-4"
+      bL1 <- require "bool-L1"
+      bL2 <- require "bool-L2"
+      bL3 <- require "bool-L3"
+      bL4 <- require "bool-L4"
+      lift $ do
+        b1  `shouldBe` True
+        b2  `shouldBe` False
+        b3  `shouldBe` True
+        b4  `shouldBe` False
+        bL1 `shouldBe` True
+        bL2 `shouldBe` False
+        bL3 `shouldBe` True
+        bL4 `shouldBe` False
+        (r "int" :: IO Bool) `shouldThrow` anyException
+    it "scientific" $ run $ \_ -> do
+      n   <- require "int"
+      n_  <- require "int-"
+      lift $ do
+        (n  :: Scientific) `shouldBe` 128
+        (n_ :: Scientific) `shouldBe` (-1)
+    it "int" $ run $ \r -> do
+      v   <- require "int"
+      v16 <- require "int"
+      v32 <- require "int"
+      v64 <- require "int"
+      vm  <- require "empty"
+      lift $ do
+        (v    :: Int)     `shouldBe` 128
+        (v16  :: Int16)   `shouldBe` 128
+        (v32  :: Int32)   `shouldBe` 128
+        (v64  :: Int64)   `shouldBe` 128
+        (vm :: Maybe Int) `shouldBe` Nothing
+        (r "int" :: IO Int8) `shouldThrow` anyException
+    it "word" $ run $ \_ -> do
+      v   <- require "int"
+      v8  <- require "int"
+      v16 <- require "int"
+      v32 <- require "int"
+      v64 <- require "int"
+      lift $ do
+        (v    :: Word)   `shouldBe` 128
+        (v8   :: Word8)  `shouldBe` 128
+        (v16  :: Word16) `shouldBe` 128
+        (v32  :: Word32) `shouldBe` 128
+        (v64  :: Word64) `shouldBe` 128
+    it "maybe" $ run $ \_ -> do
+      v  <- require "int"
+      n  <- require "int.not.found"
+      ev <- require "int"
+      en <- require "int.not.found"
+      lift $ do
+        (v :: Maybe Int)          `shouldBe` Just 128
+        (n :: Maybe Int)          `shouldBe` Nothing
+        (ev :: Either String Int) `shouldBe` Right 128
+        (en :: Either String Int) `shouldSatisfy` isLeft
+    it "text" $ run $ \_ -> do
+      v  <- require "empty"
+      vm <- require "empty"
+      n  <- require "not.found"
+      lift $ do
+        (v  :: String)       `shouldBe` ""
+        (vm :: Maybe String) `shouldBe` Just ""
+        (n  :: Maybe String) `shouldBe` Nothing
   context "placeholder" $ do
-    it "placeholder" $ do
+    it "positive" $ do
+      mkValue (VT "${}")             `shouldBe` Right (VR [VRR "" []])
+      mkValue (VT "${:${}}")         `shouldBe` Right (VR [VRR "" [VRR "" []]])
+      mkValue (VT "${a.b}")          `shouldBe` Right (VR [VRR "a.b" []])
       mkValue (VT "${xxxx}")         `shouldBe` Right (VR [VRR "xxxx" []])
       mkValue (VT "${xxxx:}")        `shouldBe` Right (VR [VRR "xxxx" [VRT ""]])
       mkValue (VT "${xxxx:7}")       `shouldBe` Right (VR [VRR "xxxx" [VRT "7"]])
@@ -48,27 +116,45 @@
       mkValue (VT "--${xxxx:7}")     `shouldBe` Right (VR [VRT "--",  VRR "xxxx" [VRT "7"]])
       mkValue (VT "${xxxx:7}\\}")    `shouldBe` Right (VR [VRR "xxxx" [VRT "7"], VRT "}"])
       mkValue (VT "128")             `shouldBe` Right (VT "128")
+      mkValue (VT "\\$")             `shouldBe` Right (VT "$")
+      mkValue (VT "\\\\")            `shouldBe` Right (VT "\\")
+      mkValue (VT "\\{")             `shouldBe` Right (VT "{")
+      mkValue (VT "\\}")             `shouldBe` Right (VT "}")
+    it "negative" $ do
+      mkValue (VT "${${}}")          `shouldSatisfy` isLeft
       mkValue (VT "${128")           `shouldSatisfy` isLeft
-    it "source" $ do
+      mkValue (VT "$")               `shouldSatisfy` isLeft
+      mkValue (VT "{}")              `shouldSatisfy` isLeft
+      mkValue (VT "}")               `shouldSatisfy` isLeft
+    it "parse placeholder" $ do
       let vals =
             [ ("hello","${hey}")
             , ("hey","girl")
             , ("ok", "${xx:girl}")
             , ("nk", "${xx}")
+            , ("world", "${hey}, world")
             , ("a", "${b}")
             , ("b", "${a}")
+            , ("c", "${:${}}")
+            , ("d", "${:}")
             ]
       loadAndRunSalak (loadMock vals) $ do
         v <- require "hello"
         x <- require "hey"
+        t <- require "world"
         y <- require "a"
         z <- require "ok"
         w <- require "nk"
+        c <- require "c"
+        d <- require "d"
         lift $ do
           (x :: String) `shouldBe` "girl"
           (v :: String) `shouldBe` "girl"
           (z :: String) `shouldBe` "girl"
+          (t :: String) `shouldBe` "girl, world"
+          (d :: String) `shouldBe` ""
           (w :: Either String String) `shouldSatisfy` isLeft
           (y :: Either String String) `shouldSatisfy` isLeft
+          (c :: Either String String) `shouldSatisfy` isLeft
 
 
diff --git a/test/Salak/Internal/SourceSpec.hs b/test/Salak/Internal/SourceSpec.hs
--- a/test/Salak/Internal/SourceSpec.hs
+++ b/test/Salak/Internal/SourceSpec.hs
@@ -1,5 +1,4 @@
-{-# LANGUAGE NoOverloadedLists #-}
-module Salak.Internal.SourceSpec where
+module Salak.Internal.SourceSpec(spec) where
 
 import           Control.Monad.Writer
 import           Data.Text             (Text, unpack)
@@ -30,6 +29,7 @@
 loadRandom :: (MonadThrow m, MonadIO m) => Text -> LoadSalakT m ()
 loadRandom key = loadList True (unpack key) go
   where
+    go :: IO [(Text, Int)]
     go = do
       a :: Int <- randomIO
       return [(key, a)]
@@ -37,7 +37,7 @@
 spec :: SpecWith ()
 spec = do
   context "source" $ do
-    it "normal - 2" $ do
+    it "normal - 1" $ do
       let (_,b,c) = extract T.empty $ gen 0 ([("hello", "world")] :: [(Text, Text)])
       length c `shouldBe` 0
       length (T.toList b) `shouldBe` 1
diff --git a/test/Salak/Internal/TrieSpec.hs b/test/Salak/Internal/TrieSpec.hs
deleted file mode 100644
--- a/test/Salak/Internal/TrieSpec.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module Salak.Internal.TrieSpec where
-
-import qualified Data.HashMap.Strict as HM
-import           Salak.Internal.Key
-import qualified Salak.Trie          as T
-import           Test.Hspec
-
-
-spec :: SpecWith ()
-spec = do
-  context "Trie" $ do
-    it "insert" $ do
-      let Right keys = toKeys ("a.b.c" :: String)
-          v          = T.insert keys (1 :: Int) T.empty
-          w          = T.alter (\_ -> Just 1) keys T.empty
-      v `shouldBe` w
-      T.getPrimitive (T.subTries keys v) `shouldBe` Just 1
-    it "toList" $ do
-      let Right keys = toKeys ("a.b.c.d" :: String)
-          w          = T.insert keys (2 :: Int) T.empty
-          [(k,v)]    = T.toList w
-      k `shouldBe` keys
-      v `shouldBe` 2
-      HM.member (KT "a") (T.getMap w) `shouldBe` True
diff --git a/test/Salak/TrieSpec.hs b/test/Salak/TrieSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Salak/TrieSpec.hs
@@ -0,0 +1,24 @@
+module Salak.TrieSpec(spec) where
+
+import qualified Data.HashMap.Strict as HM
+import           Salak.Internal.Key
+import qualified Salak.Trie          as T
+import           Test.Hspec
+
+
+spec :: SpecWith ()
+spec = do
+  context "Trie" $ do
+    it "insert" $ do
+      let Right keys = toKeys ("a.b.c" :: String)
+          v          = T.insert keys (1 :: Int) T.empty
+          w          = T.alter (\_ -> Just 1) keys T.empty
+      v `shouldBe` w
+      T.tvar (T.subTries keys v) `shouldBe` Just 1
+    it "toList" $ do
+      let Right keys = toKeys ("a.b.c.d" :: String)
+          w          = T.insert keys (2 :: Int) T.empty
+          [(k,v)]    = T.toList w
+      k `shouldBe` keys
+      v `shouldBe` 2
+      HM.member (KT "a") (T.tmap w) `shouldBe` True
diff --git a/test/SalakSpec.hs b/test/SalakSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/SalakSpec.hs
@@ -0,0 +1,2 @@
+-- file test/Spec.hs
+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=SalakSpec #-}
diff --git a/test/Spec.hs b/test/Spec.hs
deleted file mode 100644
--- a/test/Spec.hs
+++ /dev/null
@@ -1,2 +0,0 @@
--- file test/Spec.hs
-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
