diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -5,24 +5,63 @@
 [![stackage Nightly package](http://stackage.org/package/salak/badge/nightly)](http://stackage.org/nightly/package/salak)
 [![Build Status](https://travis-ci.org/leptonyu/salak.svg?branch=master)](https://travis-ci.org/leptonyu/salak)
 
+Configuration (re)loader in Haskell.
 
-Configuration Loader in Haskell.
+## salak-yaml
+[![salak-yaml](https://img.shields.io/hackage/v/salak-yaml.svg)](https://hackage.haskell.org/package/salak-yaml)
+## salak-toml
+[![salak-toml](https://img.shields.io/hackage/v/salak-toml.svg)](https://hackage.haskell.org/package/salak-toml)
 
-This library default a standard configuration load process. It can load properties from `CommandLine`, `Environment`,
-`JSON value` and `Yaml` files. They all load to the same format `SourcePack`. Earler property source has higher order
-to load property. For example:
+This library define a universal procedure to load configurations and parse properties, also supports reload configuration files.
 
+We can load configurations from command line, environment, configuration files such as yaml or toml etc, and we may want to have our own strategies to load configurations from multi sources and overwrite properties by orders of these sources.
+
+`PropConfig` defines a common loading strategy:
+> 1. loadCommandLine
+> 2. loadEnvironment
+> 3. loadConfFiles
+> 4. load file from folder `salak.conf.dir` if defined
+> 5. load file from current folder if enabled
+> 6. load file from home folder if enabled
+> 7. file extension matching, support yaml or toml or any other loader.
+
+Load earlier has higher orders, orders cannot be changed.
+
+`ReaderT SourcePack m` defines how to read properties:
+```Haskell
+require "abc.prop"
 ```
-CommandLine:  --package.a.enabled=true
-Environment: PACKAGE_A_ENABLED: false
 
-lookup "package.a.enabled" properties => Just True
+`ReloadableSourcePackT m` defines how to read reloadable properties:
+```Haskell
+requireD "abc.dynamic.prop"
 ```
 
-`CommandLine` has higher order then `Environment`, for the former load properties earler then later.
+For commandline and environment, 
+```
+CommandLine:  --package.a.enabled=true
+Environment: PACKAGE_A_ENABLED: false
+```
 
 Usage:
 
+
+Environment:
+```
+export TEST_CONFIG_NAME=daniel
+```
+Current Directory:  salak.yaml
+```YAML
+test.config:
+  name: noop
+  dir: ls
+```
+Current Directory:  salak.toml
+```TOML
+[test.config]
+ext=2
+```
+
 ```Haskell
 data Config = Config
   { name :: Text
@@ -32,16 +71,28 @@
 
 instance FromProp Config where
   fromProp = Config
-    <$> "user"
+    <$> "user" ? pattern "[a-z]{5,16}"
     <*> "pwd"
     <*> "ext" .?= 1
 
-main = do
-  c :: Config <- defaultLoadSalak def $ require ""
-  print c
+main = runSalak def { configName = Just "salak", loadExt = loadByExt $ YAML :|: TOML } $ do
+  c :: Config <- require "test.config"
+  lift $ print c
 ```
 
-```
-λ> c
-Config {name = "daniel", dir = Nothing, ext = 1}
+GHCi play
+```Haskell
+λ> import Salak
+λ> import Salak.Load.YAML
+λ> import Salak.Load.TOML
+λ> import Data.Menshen
+λ> :set -XTypeApplications
+λ> instance FromProp Config where fromProp = Config <$> "user" <*> "dir" <*> "ext" .?= 1
+λ> f = runSalak def { configName = Just "salak", loadExt = loadByExt $ YAML :|: TOML }
+λ> f (require "") >>= print @Config
+Config {name = "daniel", dir = Just "ls", ext = 2}
 ```
+
+
+TODO:
+- Add git pull support.
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.2.6
+version: 0.2.7
 license: BSD3
 license-file: LICENSE
 copyright: (c) 2018 Daniel YU
@@ -8,41 +8,38 @@
 author: Daniel YU
 homepage: https://github.com/leptonyu/salak#readme
 synopsis: Configuration Loader
-category: Library
+category: Library, Configuration
 build-type: Simple
 extra-source-files:
     README.md
-    test/salak.yml
 
 library
     exposed-modules:
         Salak
+        Salak.Load
     hs-source-dirs: src
     other-modules:
         Salak.Types
-        Salak.Env
-        Salak.Json
+        Salak.Types.Value
+        Salak.Types.Selector
+        Salak.Types.Source
+        Salak.Load.Env
+        Salak.Load.Dynamic
         Salak.Prop
-        Salak.Dynamic
     default-language: Haskell2010
-    ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures
     build-depends:
-        aeson <1.5,
-        attoparsec <0.14,
-        base >=4.7 && <5,
-        containers <0.7,
-        data-default <0.8,
-        directory <1.4,
-        filepath <1.5,
-        menshen <0.1,
-        mtl <2.3,
-        pqueue <1.5,
-        scientific <0.4,
-        text <1.3,
-        transformers <0.6,
-        unordered-containers <0.3,
-        vector <0.13,
-        yaml <0.12
+        attoparsec >=0.13.2.2 && <0.14,
+        base >=4.10 && <5,
+        containers >=0.6.0.1 && <0.7,
+        data-default >=0.7.1.1 && <0.8,
+        directory >=1.3.3.0 && <1.4,
+        filepath >=1.4.2.1 && <1.5,
+        menshen >=0.0.2 && <0.1,
+        mtl >=2.2.2 && <2.3,
+        pqueue >=1.4.1.2 && <1.5,
+        scientific >=0.3.6.2 && <0.4,
+        text >=1.2.3.1 && <1.3,
+        time >=1.8.0.2 && <1.9
 
 test-suite spec
     type: exitcode-stdio-1.0
@@ -50,30 +47,30 @@
     hs-source-dirs: test src
     other-modules:
         Salak
-        Salak.Dynamic
-        Salak.Env
-        Salak.Json
+        Salak.Load
+        Salak.Load.Dynamic
+        Salak.Load.Env
         Salak.Prop
         Salak.Types
+        Salak.Types.Selector
+        Salak.Types.Source
+        Salak.Types.Value
         Paths_salak
     default-language: Haskell2010
-    ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures
     build-depends:
-        QuickCheck <2.14,
-        aeson <1.5,
-        attoparsec <0.14,
-        base >=4.7 && <5,
-        containers <0.7,
-        data-default <0.8,
-        directory <1.4,
-        filepath <1.5,
+        QuickCheck >=2.12.6.1 && <2.14,
+        attoparsec >=0.13.2.2 && <0.14,
+        base >=4.10 && <5,
+        containers >=0.6.0.1 && <0.7,
+        data-default >=0.7.1.1 && <0.8,
+        directory >=1.3.3.0 && <1.4,
+        filepath >=1.4.2.1 && <1.5,
         hspec ==2.*,
-        menshen <0.1,
-        mtl <2.3,
-        pqueue <1.5,
-        scientific <0.4,
-        text <1.3,
-        transformers <0.6,
-        unordered-containers <0.3,
-        vector <0.13,
-        yaml <0.12
+        menshen >=0.0.2 && <0.1,
+        mtl >=2.2.2 && <2.3,
+        pqueue >=1.4.1.2 && <1.5,
+        salak-toml >=0.2.7 && <0.3,
+        salak-yaml >=0.2.7 && <0.3,
+        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
@@ -1,7 +1,9 @@
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RankNTypes        #-}
-{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE FlexibleInstances   #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE RankNTypes          #-}
+{-# LANGUAGE RecordWildCards     #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeOperators       #-}
 -- |
 -- Module:      Salak
 -- Copyright:   (c) 2019 Daniel YU
@@ -17,18 +19,19 @@
   -- $use
 
   -- * Salak
-    defaultLoadSalak
-  , loadSalak
+    loadAndRunSalak
+  , runSalak
   , PropConfig(..)
-  -- * Static Load
+  -- * Static Get Properties
   , HasSourcePack(..)
   , fetch
   , require
-  -- * Dynamic Load
+  -- * Dynamic Get Properties
   , ReloadableSourcePack
   , ReloadableSourcePackT
   , ReloadResult(..)
   , reloadable
+  , reloadAction
   , fetchD
   , requireD
   -- * Prop Parser
@@ -40,14 +43,21 @@
   -- * SourcePack
   , SourcePack
   , SourcePackT
-  , loadYaml
+  -- * Load configurations
   , loadCommandLine
   , loadEnv
   , loadMock
-  , defaultParseCommandLine
+  -- ** Load By Extension
+  , ExtLoad
+  , loadByExt
+  , HasLoad(..)
+  , (:|:)(..)
+  -- * Other
   , ParseCommandLine
+  , defaultParseCommandLine
   , Priority
   , Value(..)
+  , defaultLoadSalak
   ) where
 
 import           Control.Applicative
@@ -57,11 +67,11 @@
 import           Control.Monad.State
 import           Data.Default
 import           Data.Text              (Text)
-import           Salak.Dynamic
-import           Salak.Env
-import           Salak.Json
+import           Salak.Load.Dynamic
+import           Salak.Load.Env
 import           Salak.Prop
 import           Salak.Types
+import           Salak.Types.Value
 import           System.Directory
 import           System.FilePath        ((</>))
 
@@ -72,47 +82,93 @@
   , searchCurrent :: Bool          -- ^ Search current directory, default true
   , searchHome    :: Bool          -- ^ Search home directory, default false.
   , commandLine   :: ParseCommandLine -- ^ How to parse commandline
+  , loadExt       :: FilePath -> SourcePackT IO ()
   }
 
 instance Default PropConfig where
-  def = PropConfig Nothing "salak.conf" True False defaultParseCommandLine
+  def = PropConfig
+    Nothing
+    "salak.conf.dir"
+    True
+    False
+    defaultParseCommandLine
+    (\_ -> return ())
 
--- | Load salak `SourcePack` and fetch properties.
-loadSalak
+-- | Load and run salak `SourcePack` and fetch properties.
+loadAndRunSalak
   :: Monad m
-  => ReaderT SourcePack m a -- ^ Fetch properties monad.
-  -> SourcePackT m ()       -- ^ Load properties monad.
+  => SourcePackT m ()       -- ^ Load properties monad.
+  -> ReaderT SourcePack m a -- ^ Fetch properties monad.
   -> m a
-loadSalak a spm = do
-  (es, sp) <- runSourcePackT spm
+loadAndRunSalak spm a = do
+  sp <- runSourcePackT spm
+  let es = errs sp
   unless (null es) $ fail (head es)
   runReaderT a sp
 
+
+-- | Load file by extension
+type ExtLoad = (String, FilePath -> SourcePackT IO ())
+
+class HasLoad a where
+  loaders :: a -> [ExtLoad]
+
+data a :|: b = a :|: b
+infixr 3 :|:
+
+instance (HasLoad a, HasLoad b) => HasLoad (a :|: b) where
+  loaders (a :|: b) = loaders a ++ loaders b
+
+loadByExt :: (HasLoad a, MonadIO m) => a -> FilePath -> SourcePackT m ()
+loadByExt xs f = mapM_ go (loaders xs)
+  where
+    go (ext, ly) = tryLoadFile (jump . ly) $ f ++ "." ++ ext
+
+jump :: MonadIO m => StateT SourcePack IO a -> StateT SourcePack m ()
+jump a = get >>= lift . liftIO . execStateT a >>= put
+
 -- | Default load salak.
--- All these configuration sources has orders, from highest order to lowest order:
+-- All these configuration sources has orders, from highest priority to lowest priority:
 --
--- > 1. CommandLine
--- > 2. Environment
--- > 3. Specified Yaml file(file in `configDirKey`)
--- > 4. Yaml file in current directory
--- > 5. Yaml file in home directory
+-- > 1. loadCommandLine
+-- > 2. loadEnvironment
+-- > 3. loadConfFiles
+-- > 4. load file from folder `salak.conf.dir` if defined
+-- > 5. load file from current folder if enabled
+-- > 6. load file from home folder if enabled
+-- > 7. file extension matching, support yaml or toml or any other loader.
 --
-defaultLoadSalak :: MonadIO m => PropConfig -> ReaderT SourcePack m a -> m a
-defaultLoadSalak PropConfig{..} a = loadSalak a $ do
+runSalak :: MonadIO m => PropConfig -> ReaderT SourcePack m a -> m a
+runSalak PropConfig{..} = loadAndRunSalak $ do
   loadCommandLine commandLine
   loadEnv
-  cf <- fetch configDirKey
-  maybe (return ()) (go cf) configName
+  forM_ configName $ forM_
+    [ require configDirKey
+    , ifS searchCurrent getCurrentDirectory
+    , ifS searchHome    getHomeDirectory
+    ] . loadConf
   where
-    go ck n = do
-      case ck of
-        Left  _ -> return ()
-        Right d -> loadYaml $ d </> n
-      c <- liftIO getCurrentDirectory
-      when searchCurrent $ tryLoadYaml $ c </> n
-      h <- liftIO getHomeDirectory
-      when searchHome    $ tryLoadYaml $ h </> n
+    ifS True gxd = Just <$> liftIO gxd
+    ifS _    _   = return Nothing
+    loadConf n mf = mf >>= mapM_ (jump . loadExt . (</> n))
 
+{-# DEPRECATED defaultLoadSalak "use runSalak instead" #-}
+defaultLoadSalak :: MonadIO m => PropConfig -> ReaderT SourcePack m a -> m a
+defaultLoadSalak = runSalak
+
+
+  --   loadC ffa n = (mapM_ . mapM_) g
+  -- cf <- fetch configDirKey
+  -- where
+  --   go ck n = do
+  --     case ck of
+  --       Left  _ -> return ()
+  --       Right d -> defaultLoadByExt $ d </> n
+  --     c <- liftIO getCurrentDirectory
+  --     when searchCurrent $ defaultLoadByExt $ c </> n
+  --     h <- liftIO getHomeDirectory
+  --     when searchHome    $ defaultLoadByExt $ h </> n
+
 class Monad m => HasSourcePack m where
   askSourcePack :: m SourcePack
 
@@ -173,19 +229,42 @@
 
 -- $use
 --
--- | This library default a standard configuration load process. It can load properties from `CommandLine`, `Environment`,
--- `JSON value` and `Yaml` files. They all load to the same format `SourcePack`. Earler property source has higher order
--- to load property. For example:
+-- | This library define a universal procedure to load configurations and parse properties, also supports reload configuration files.
 --
--- > CommandLine:  --package.a.enabled=true
--- > Environment: PACKAGE_A_ENABLED: false
 --
--- > lookup "package.a.enabled" properties => Just True
+-- We can load configurations from command line, environment, configuration files such as yaml or toml etc, and we may want to have our own strategies to load configurations from multi sources and overwrite properties by orders of these sources.
 --
--- `CommandLine` has higher order then `Environment`, for the former load properties earler then later.
+-- `PropConfig` defines a common loading strategy:
 --
+-- > 1. loadCommandLine
+-- > 2. loadEnvironment
+-- > 3. loadConfFiles
+-- > 4. load file from folder `salak.conf.dir` if defined
+-- > 5. load file from current folder if enabled
+-- > 6. load file from home folder if enabled
+-- > 7. file extension matching, support yaml or toml or any other loader.
+--
+-- Load earlier has higher priority, priorities cannot be changed.
+--
+--
 -- Usage:
 --
+--
+-- Environment:
+--
+-- > export TEST_CONFIG_NAME=daniel
+--
+-- Current Directory:  salak.yaml
+--
+-- > test.config:
+-- >   name: noop
+-- >   dir: ls
+--
+-- Current Directory:  salak.toml
+--
+-- > [test.config]
+-- > ext=2
+--
 -- > data Config = Config
 -- >   { name :: Text
 -- >   , dir  :: Maybe Text
@@ -194,13 +273,22 @@
 -- >
 -- > instance FromProp Config where
 -- >   fromProp = Config
--- >     <$> "user"
+-- >     <$> "user" ? pattern "[a-z]{5,16}"
 -- >     <*> "pwd"
 -- >     <*> "ext" .?= 1
+-- >
+-- > main = runSalak def { configName = Just "salak", loadExt = loadByExt $ YAML :|: TOML } $ do
+-- >   c :: Config <- require "test.config"
+-- >   lift $ print c
 --
--- > main = do
--- >   c :: Config <- defaultLoadSalak def $ require ""
--- >   print c
+-- GHCi play
 --
--- > λ> c
--- > Config {name = "daniel", dir = Nothing, ext = 1}
+-- > λ> import Salak
+-- > λ> import Salak.Load.YAML
+-- > λ> import Salak.Load.TOML
+-- > λ> :set -XTypeApplications
+-- > λ> instance FromProp Config where fromProp = Config <$> "user" <*> "dir" <*> "ext" .?= 1
+-- > λ> f = runSalak def { configName = Just "salak", loadExt = loadByExt $ YAML :|: TOML }
+-- > λ> f (require "") >>= print @Config
+-- > Config {name = "daniel", dir = Just "ls", ext = 2}
+--
diff --git a/src/Salak/Dynamic.hs b/src/Salak/Dynamic.hs
deleted file mode 100644
--- a/src/Salak/Dynamic.hs
+++ /dev/null
@@ -1,71 +0,0 @@
-{-# LANGUAGE RecordWildCards #-}
-{-# LANGUAGE TupleSections   #-}
-module Salak.Dynamic where
-
-import           Control.Concurrent.MVar
-import           Control.Monad.IO.Class  (MonadIO, liftIO)
-import           Control.Monad.State
-import qualified Data.IntMap.Strict      as MI
-import           Data.Text               (Text)
-import           Salak.Prop
-import           Salak.Types
-
-data ReloadResult = ReloadResult
-  { isError :: Bool
-  , msg     :: [String]
-  } deriving (Eq, Show)
-
--- | Reloadable SourcePack
-data ReloadableSourcePack = ReloadableSourcePack
-  { sourcePack :: MVar SourcePack
-  , reloadAll  :: (SourcePack -> IO ([IO ()], [String])) -> IO ReloadResult
-  }
-
-reloadableSourcePack :: MonadIO m => SourcePack -> m ReloadableSourcePack
-reloadableSourcePack sp = do
-  msp <- liftIO $ newMVar sp
-  return $ ReloadableSourcePack msp (reloadAll' msp)
-  where
-    reloadAll' v f = do
-      sp'@(SourcePack _ _ _ it) <- readMVar v
-      as <- filter (not . nullSource . snd) <$> mapM go (MI.toList it)
-      if null as
-        then return (ReloadResult True [])
-        else do
-          let (es, sp'') = extractErr' $ foldl g2 sp' as
-          (ac, es') <- f sp''
-          if null es'
-            then putMVar v sp'' >>  sequence_ ac >> return (ReloadResult True es)
-            else return (ReloadResult False es')
-    go (i, Reload _ f) = (i,) <$> f i
-    g2 (SourcePack ss i s it) (x,s') = SourcePack ss i (replace x s' s) it
-
-type ReloadableSourcePackT = StateT ReloadableSourcePack
-
-search' :: (MonadIO m, FromProp a) => Text -> ReloadableSourcePackT m (Either String (IO a))
-search' k = do
-  ReloadableSourcePack{..}  <- get
-  sp <- liftIO $ takeMVar sourcePack
-  case search k sp of
-    Left  e -> return (Left e)
-    Right r -> do
-      v  <- liftIO $ newMVar r
-      put (ReloadableSourcePack sourcePack (reloadAll . go v))
-      return $ Right $ readMVar v
-  where
-    go x f sp = do
-      (as,es) <- f sp
-      case search k sp of
-        Left  e -> return (as, e:es)
-        Right r -> return (putMVar x r:as, es)
-
-reload :: Monad m => ReloadableSourcePackT m (IO ReloadResult)
-reload = do
-  ReloadableSourcePack{..} <- get
-  return $ reloadAll $ \_ -> return ([], [])
-
-runReloadable :: MonadIO m => ReloadableSourcePackT m a -> SourcePack -> m a
-runReloadable r sp = reloadableSourcePack sp >>= evalStateT r
-
-
-
diff --git a/src/Salak/Env.hs b/src/Salak/Env.hs
deleted file mode 100644
--- a/src/Salak/Env.hs
+++ /dev/null
@@ -1,34 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module Salak.Env where
-
-import           Control.Monad.IO.Class (MonadIO, liftIO)
-import           Control.Monad.State
-import           Data.Maybe
-import qualified Data.Text              as T
-import           Salak.Types
-import           System.Environment
-
-loadEnv :: MonadIO m => SourcePackT m ()
-loadEnv = do
-  args <- liftIO getEnvironment
-  modify $ load (emptyReload "environment") args go
-  where
-    go p (k,v) = (g2 k, VStr p $ T.pack v)
-    g2 = T.toLower . T.pack . map (\c -> if c == '_' then '.' else c)
-
-type ParseCommandLine = [String] -> IO [(T.Text,Priority -> Value)]
-
-defaultParseCommandLine :: ParseCommandLine
-defaultParseCommandLine = return . mapMaybe go
-  where
-    go ('-':'-':as) = case break (=='=') as of
-      (a,'=':b) -> Just (T.pack a, \p -> VStr p $ T.pack b)
-      _         -> Nothing
-    go _ = Nothing
-
-loadCommandLine :: MonadIO m => ParseCommandLine -> SourcePackT m ()
-loadCommandLine pcl = do
-  args <- liftIO $ getArgs >>= pcl
-  modify $ load (emptyReload "commandline") args go
-  where
-    go i (k,fv) = (k, fv i)
diff --git a/src/Salak/Json.hs b/src/Salak/Json.hs
deleted file mode 100644
--- a/src/Salak/Json.hs
+++ /dev/null
@@ -1,38 +0,0 @@
-module Salak.Json where
-
-import           Control.Monad.IO.Class (MonadIO, liftIO)
-import           Control.Monad.State
-import qualified Data.Aeson             as A
-import qualified Data.HashMap.Strict    as HM
-import qualified Data.IntMap.Strict     as MI
-import qualified Data.Map.Strict        as M
-import           Data.Maybe
-import           Data.Text              (pack)
-import qualified Data.Vector            as V
-import qualified Data.Yaml              as Y
-import           Salak.Types
-import           System.Directory
-
-loadJSON :: Reload -> A.Value -> SourcePack -> SourcePack
-loadJSON name v sp = loadFile name sp $ go v
-  where
-    go (A.Object m) i s = foldl (g2 i) s $ HM.toList m
-    go (A.Array  m) i s = foldl (g3 i) s $ zip [0..] $ V.toList m
-    go (A.String m) i s = insert' [] (VStr  i m) s
-    go (A.Number m) i s = insert' [] (VNum  i m) s
-    go (A.Bool   m) i s = insert' [] (VBool i m) s
-    go A.Null       _ s = s
-    g2 i (Source es p is ts) (k,v') = let s' = go v' i (fromMaybe emptySource $  M.lookup k ts) in Source es p is  $ M.insert k s' ts
-    g3 i (Source es p is ts) (k,v') = let s' = go v' i (fromMaybe emptySource $ MI.lookup k is) in Source es p (MI.insert k s' is) ts
-
-loadYaml :: MonadIO m => FilePath -> SourcePackT m ()
-loadYaml file = do
-  v <- liftIO $ Y.decodeFileEither file
-  modify $ \sp -> case v of
-    Left  e -> addErr' (show e) sp
-    Right a -> loadJSON (emptyReload $ pack file) a sp
-
-tryLoadYaml :: MonadIO m => FilePath -> SourcePackT m ()
-tryLoadYaml file = do
-  b <- liftIO $ doesFileExist file
-  when b $ loadYaml file
diff --git a/src/Salak/Load.hs b/src/Salak/Load.hs
new file mode 100644
--- /dev/null
+++ b/src/Salak/Load.hs
@@ -0,0 +1,34 @@
+-- |
+-- Module:      Salak
+-- Copyright:   (c) 2019 Daniel YU
+-- License:     BSD3
+-- Maintainer:  leptonyu@gmail.com
+-- Stability:   experimental
+-- Portability: portable
+--
+-- This module is designed for implementating configuration file loading.
+--
+module Salak.Load(
+  -- * Reload
+    Reload(..)
+  , defReload
+  -- * SourcePack
+  , Source
+  , SourcePack
+  , SourcePackT
+  , addErr'
+  -- * Selector
+  , Selector(..)
+  , simpleSelectors
+  -- * Source
+  , insertSource
+  , updateSources
+  , updateSource
+  -- * Load
+  , tryLoadFile
+  , loadFile
+  ) where
+
+import           Salak.Types
+import           Salak.Types.Selector
+import           Salak.Types.Source
diff --git a/src/Salak/Load/Dynamic.hs b/src/Salak/Load/Dynamic.hs
new file mode 100644
--- /dev/null
+++ b/src/Salak/Load/Dynamic.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TupleSections   #-}
+module Salak.Load.Dynamic where
+
+import           Control.Concurrent.MVar
+import           Control.Monad.IO.Class  (MonadIO, liftIO)
+import           Control.Monad.State
+import           Control.Monad.Writer
+import qualified Data.IntMap.Strict      as MI
+import           Data.Text               (Text)
+import           Salak.Prop
+import           Salak.Types
+import           Salak.Types.Source
+
+data ReloadResult = ReloadResult
+  { isError :: Bool
+  , msg     :: [String]
+  } deriving (Eq, Show)
+
+-- | Reloadable SourcePack
+data ReloadableSourcePack = ReloadableSourcePack
+  { sourcePack :: MVar SourcePack
+  , reloadAll  :: (SourcePack -> IO ([IO ()], [String])) -> IO ReloadResult
+  }
+
+reloadableSourcePack :: MonadIO m => SourcePack -> m ReloadableSourcePack
+reloadableSourcePack sp = do
+  msp <- liftIO $ newMVar sp
+  return $ ReloadableSourcePack msp (reloadAll' msp)
+  where
+    reloadAll' v f = do
+      sp' <- readMVar v
+      as  <- sequence $ MI.foldlWithKey' go [] (reEnv sp')
+      let loadErr = concat $ fst . snd <$> as
+          runWith e a = if null e then a else return $ ReloadResult True e
+      runWith loadErr $ do
+        let sp''    = foldl g2 sp' {errs = []} as
+            modLog  = errs sp''
+        (ac, msErr) <- f sp''
+        runWith msErr $ putMVar v sp'' >>  sequence_ ac >> return (ReloadResult False modLog)
+    go b i (Reload _ f) = ((i,) <$> f i) : b
+    g2 :: SourcePack -> (Int, ([String], Source)) -> SourcePack
+    g2 p (i, (_, s)) = let (s', e) = runWriter $ replace i s (source p) in p { source = s', errs = errs p <> e}
+
+type ReloadableSourcePackT = StateT ReloadableSourcePack
+
+search' :: (MonadIO m, FromProp a) => Text -> ReloadableSourcePackT m (Either String (IO a))
+search' k = do
+  ReloadableSourcePack{..}  <- get
+  sp <- liftIO $ takeMVar sourcePack
+  case search k sp of
+    Left  e -> return (Left e)
+    Right r -> do
+      v  <- liftIO $ newMVar r
+      put (ReloadableSourcePack sourcePack (reloadAll . go v))
+      return $ Right $ readMVar v
+  where
+    go x f sp = do
+      (as,es) <- f sp
+      case search k sp of
+        Left  e -> return (as, e:es)
+        Right r -> return (putMVar x r:as, es)
+
+reloadAction :: Monad m => ReloadableSourcePackT m (IO ReloadResult)
+reloadAction = do
+  ReloadableSourcePack{..} <- get
+  return $ reloadAll $ \_ -> return ([], [])
+
+runReloadable :: MonadIO m => ReloadableSourcePackT m a -> SourcePack -> m a
+runReloadable r sp = reloadableSourcePack sp >>= evalStateT r
+
+
+
diff --git a/src/Salak/Load/Env.hs b/src/Salak/Load/Env.hs
new file mode 100644
--- /dev/null
+++ b/src/Salak/Load/Env.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Salak.Load.Env where
+
+import           Control.Monad.IO.Class (MonadIO, liftIO)
+import           Control.Monad.State
+import           Data.Maybe
+import qualified Data.Text              as T
+import           Salak.Types
+import           Salak.Types.Value
+import           System.Environment
+
+loadEnv :: MonadIO m => SourcePackT m ()
+loadEnv = do
+  args <- liftIO getEnvironment
+  modify $ load (emptyReload "environment") args go
+  where
+    go p (k,v) = (g2 k, VStr p $ T.pack v)
+    g2 = T.toLower . T.pack . map (\c -> if c == '_' then '.' else c)
+
+type ParseCommandLine = [String] -> IO [(T.Text,Priority -> Value)]
+
+defaultParseCommandLine :: ParseCommandLine
+defaultParseCommandLine = return . mapMaybe go
+  where
+    go ('-':'-':as) = case break (=='=') as of
+      (a,'=':b) -> Just (T.pack a, \p -> VStr p $ T.pack b)
+      _         -> Nothing
+    go _ = Nothing
+
+loadCommandLine :: MonadIO m => ParseCommandLine -> SourcePackT m ()
+loadCommandLine pcl = do
+  args <- liftIO $ getArgs >>= pcl
+  modify $ load (emptyReload "commandline") args go
+  where
+    go i (k,fv) = (k, fv i)
diff --git a/src/Salak/Prop.hs b/src/Salak/Prop.hs
--- a/src/Salak/Prop.hs
+++ b/src/Salak/Prop.hs
@@ -5,6 +5,7 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE MultiParamTypeClasses      #-}
 {-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE RecordWildCards            #-}
 {-# LANGUAGE ScopedTypeVariables        #-}
 {-# LANGUAGE TypeOperators              #-}
 {-# LANGUAGE TypeSynonymInstances       #-}
@@ -14,9 +15,8 @@
 import           Control.Applicative
 import           Control.Monad.Reader
 import           Data.Int
-import qualified Data.IntMap.Strict   as MI
+import qualified Data.Map.Strict      as M
 import           Data.Menshen
-import qualified Data.PQueue.Min      as Q
 import           Data.Scientific
 import           Data.Text            (Text)
 import qualified Data.Text            as T
@@ -26,6 +26,9 @@
 import           GHC.Generics         hiding (Selector)
 import qualified GHC.Generics         as G
 import           Salak.Types
+import           Salak.Types.Selector
+import           Salak.Types.Source
+import           Salak.Types.Value
 import           Text.Read            (readMaybe)
 
 data PResult a
@@ -93,7 +96,7 @@
 instance {-# OVERLAPPABLE #-} (G.Selector s, GFromProp a) => GFromProp (M1 S s a) where
   gFromProp = local go $ M1 <$> gFromProp
     where
-      go sp = select sp (STxt $ T.pack $ selName (undefined :: t s a p))
+      go sp = select sp (SStr $ T.pack $ selName (undefined :: t s a p))
 
 instance {-# OVERLAPPABLE #-} GFromProp a => GFromProp (M1 D i a) where
   gFromProp = M1 <$> gFromProp
@@ -120,11 +123,12 @@
 
 instance {-# OVERLAPPABLE #-} FromProp a => FromProp [a] where
   fromProp = do
-    SourcePack ss i (Source _ _ is _) it <- ask
-    foldM (go ss i it) [] $ MI.toList is
+    sp@SourcePack{..} <- ask
+    as <- foldM (go sp) [] $ M.toList (mapValue source)
+    return (reverse as)
     where
-      go xx x xt as (ix,s) = do
-        a <- lift $ runProp (SourcePack (SNum ix:xx) x s xt) fromProp
+      go sp' as (ix,s) = do
+        a <- lift $ runProp sp' { prefix = ix : prefix sp', source = s} fromProp
         return (a:as)
 
 instance {-# OVERLAPPABLE #-} FromEnumProp a => FromProp a where
@@ -132,16 +136,15 @@
     VStr  _ s -> case fromEnumProp $ T.toLower s of
       Left  e -> F ss e
       Right r -> O ss r
-    VNum  _ _ -> F ss "number cannot be enum"
-    VBool _ _ -> F ss "bool   cannot be enum"
+    x         -> F ss $ getType x ++ " cannot be enum"
 
 -- | ReadPrimitive value
 readPrimitive :: ([Selector] -> Value -> PResult a) -> Prop a
 readPrimitive f = do
-  SourcePack ss _ (Source _ q _ _) _ <- ask
-  case Q.getMin q of
-    Just v -> lift $ f ss v
-    _      -> lift $ N ss
+  SourcePack{..}<- ask
+  case getQ (value source) of
+    Just v -> lift $ f prefix v
+    _      -> lift $ N prefix
 
 class FromEnumProp a where
   fromEnumProp :: Text -> Either String a
@@ -149,8 +152,8 @@
 
 err :: String -> Prop a
 err e = do
-  SourcePack ss _ _ _ <- ask
-  lift $ F ss e
+  sp <- ask
+  lift $ F (prefix sp) e
 
 -- | Parse value
 readSelect :: FromProp a => Text -> Prop a
@@ -168,20 +171,19 @@
   fromProp = readPrimitive go
     where
       go s (VBool _ x) = O s x
-      go s (VNum  _ _) = F s "number cannot be bool"
       go s (VStr  _ x) = case T.toLower x of
         "true"  -> O s True
         "yes"   -> O s True
         "false" -> O s False
         "no"    -> O s False
         _       -> F s "string convert bool failed"
+      go s x           = F s $ getType x ++ " cannot be bool"
 
 instance FromProp Text where
   fromProp = readPrimitive go
     where
       go s (VStr  _ x) = O s x
-      go s (VBool _ _) = F s "boolean cannot be string"
-      go s (VNum  _ _) = F s "number cannot be string"
+      go s x           = O s $ T.pack (getV x)
 
 instance FromProp TL.Text where
   fromProp = TL.fromStrict <$> fromProp
@@ -196,7 +198,7 @@
         Just v -> O s v
         _      -> F s "string convert number failed"
       go s (VNum  _ x) = O s x
-      go s (VBool _ _) = F s "boolean cannot be number"
+      go s x           = F s $ getType x ++ " cannot be number"
 
 instance FromProp Float where
   fromProp = toRealFloat <$> fromProp
diff --git a/src/Salak/Types.hs b/src/Salak/Types.hs
--- a/src/Salak/Types.hs
+++ b/src/Salak/Types.hs
@@ -1,204 +1,81 @@
-{-# LANGUAGE DeriveFunctor        #-}
 {-# LANGUAGE FlexibleInstances    #-}
 {-# LANGUAGE OverloadedStrings    #-}
-{-# LANGUAGE TupleSections        #-}
+{-# LANGUAGE RecordWildCards      #-}
 {-# LANGUAGE TypeSynonymInstances #-}
 module Salak.Types where
 
-import           Control.Applicative  ((<|>))
 import           Control.Monad.State
-import           Data.Attoparsec.Text
+import           Control.Monad.Writer
 import qualified Data.IntMap.Strict   as MI
-import           Data.List            (intercalate)
-import qualified Data.Map.Strict      as M
-import           Data.Maybe
-import qualified Data.PQueue.Min      as Q
-import           Data.Scientific      (Scientific)
 import           Data.Text            (Text)
 import qualified Data.Text            as T
-
-type Priority = Int
-
-data Value
-  = VStr  Priority !Text
-  | VNum  Priority !Scientific
-  | VBool Priority !Bool
-  deriving (Eq, Show, Ord)
-
-type QV = Q.MinQueue Value
-
-getPriority :: Value -> Priority
-getPriority (VStr  p _) = p
-getPriority (VNum  p _) = p
-getPriority (VBool p _) = p
+import           Salak.Types.Selector
+import           Salak.Types.Source
+import           Salak.Types.Value
+import           System.Directory
 
 data Reload = Reload
   { sourceName :: Text
-  , reload     :: Priority -> IO Source
+  , reload     :: Priority -> IO ([String], Source)
   }
 
 instance Show Reload where
   show (Reload s _) = T.unpack s
 
-emptyReload :: Text -> Reload
-emptyReload s = Reload s (\_ -> return emptySource)
-
-type PriorityEnv = MI.IntMap Reload
-
-nullSource :: Source -> Bool
-nullSource (Source _ q is ts) = Q.null q && MI.null is && M.null ts
-
-getQ :: Source -> QV
-getQ (Source _ q _ _) = q
-
-replaceQ :: String -> Priority -> QV -> QV -> ([String], QV)
-replaceQ s i nq q =
-  let (a,b) = Q.partition ((==i) . getPriority) q
-  in if a == nq then ([], q) else case Q.getMin nq of
-    Just v -> ([ (if Q.null a then "Add " else "Mod ") ++ s], Q.insert v b)
-    _      -> (if Q.null a then [] else ["Del " ++ s], b)
-
-data Source' v = Source [String] v (MI.IntMap (Source' v)) (M.Map Text (Source' v)) deriving (Eq, Functor)
-
-type Source = Source' QV
-
-instance Show Source where
-  show = unlines . go ""
-    where
-      go p (Source _ q is ts) = (if Q.null q then [] else [ p ++ "=" ++ show q ])
-        ++ concat ((\(k,v) -> go (p ++ "[" ++ show k ++ "]") v) <$> MI.toList is)
-        ++ concat ((\(k,v) -> go (if null p then T.unpack k else p ++ "." ++ T.unpack k) v) <$> M.toList  ts)
-
-emptySource :: Source
-emptySource = Source [] Q.empty MI.empty M.empty
-
-instance Foldable Source' where
-  foldr f b s@(Source _ _ is ts) = foldr go (foldr go (go s b) is) ts
-    where
-      go (Source _ q _ _) = f q
-
-foldSource :: (Value -> b -> b) -> b -> Source -> b
-foldSource f = foldr (\q b -> maybe b (`f` b) $ Q.getMin q)
-
-sizeSouce :: Source -> Int
-sizeSouce = foldSource (const (+1)) 0
-
-extractErr :: Source -> ([String], Source)
-extractErr (Source es q is ts) =
-  let (ise, is') = MI.mapAccum go es  is
-      (tse, ts') =  M.mapAccum go ise ts
-  in (tse, Source [] q is' ts')
-  where
-    go e s = let (e', s') = extractErr s in (e ++ e', s')
-
-replace = replace' []
-
-replace' :: [Selector] -> Priority -> Source -> Source -> Source
-replace' ss i (Source _ nq nis nts) (Source es q is ts) =
-  let (ms, q')  = replaceQ (toKey ss) i nq q
-      (isa,isb) = MI.partition nullSource $ MI.mapWithKey (g2.SNum) $ MI.unionWithKey (go.SNum) (f 0 nis) (f 1 is)
-      (tsa,tsb) =  M.partition nullSource $  M.mapWithKey (g2.STxt) $  M.unionWithKey (go.STxt) (f 0 nts) (f 1 ts)
-  in Source (g $ Source (es ++ ms) Q.empty isa tsa) q' isb tsb
-  where
-    f x = ((x::Int,) <$>)
-    g   = fst . extractErr
-    go st (_, s) (_, s') = (2, replace' (st:ss) i s s')
-    g2 st (0, s) = replace' (st:ss) i s emptySource
-    g2 st (1, s) = replace' (st:ss) i emptySource s
-    g2 _  (_, s) = s
-
-data Selector
-  = STxt !Text
-  | SNum !Int
-  deriving Eq
-
-instance Show Selector where
-  show (STxt x) = T.unpack x
-  show (SNum i) = "[" ++ show i ++ "]"
-
-toKey :: [Selector] -> String
-toKey = intercalate "." . go . reverse
-  where
-    go (a@(STxt _):b@(SNum _):cs) = (show a ++ show b) : go cs
-    go (a:bs)                     = show a : go bs
-    go []                         = []
-
-selectors :: Text -> Either String [Selector]
-selectors = go . parse exprs . flip T.snoc '\n'
-  where
-    go (Done i r) = if i /= "\n" then Left $ "uncomplete parse" ++ T.unpack i else Right r
-    go a          = Left (show a)
-
-exprs :: Parser [Selector]
-exprs = concat <$> ( (expr <|> return []) `sepBy` char '.')
-
--- xx
--- xx.xx
--- xx.xx[0]
--- xx.xx[1].xx
-expr :: Parser [Selector]
-expr = do
-  name <- T.pack <$> do
-    a <- choice [letter, digit]
-    b <- many' (choice [letter, digit, char '-',  char '_'])
-    return (a:b)
-  (paren decimal >>= \i -> return [STxt name, SNum i]) <|> return [STxt name]
+defReload :: String -> SourcePackT IO () -> Reload
+defReload s spt = Reload (T.pack s) (\i -> go <$> execStateT spt emptySourcePack {packId = i})
   where
-    paren e = do
-      _  <- char '['
-      ex <- e
-      _  <- char ']'
-      return ex
-
-addErr :: String -> Source -> Source
-addErr e (Source es a b c) = Source (e:es) a b c
-
-insert :: Text -> Value -> Source -> Source
-insert k v s = case selectors k of
-  Left  e  -> addErr e s
-  Right k' -> insert' k' v s
+    go SourcePack{..} = (errs, source)
 
-insert' :: [Selector] -> Value -> Source -> Source
-insert' [] v (Source es q is ts) = Source es (Q.insert v q) is ts
-insert' (STxt n:ss) v (Source es q is ts) = Source es q is $ M.alter (Just . insert' ss v . fromMaybe emptySource) n ts
-insert' (SNum i:ss) v (Source es q is ts) = Source es q (MI.alter (Just . insert' ss v . fromMaybe emptySource) i is) ts
+emptyReload s = defReload s (return ())
 
-data SourcePack = SourcePack [Selector] Int Source PriorityEnv deriving Show
+data SourcePack = SourcePack
+  { prefix :: [Selector]
+  , packId :: Int
+  , source :: Source
+  , reEnv  :: MI.IntMap Reload
+  , errs   :: [String]
+  } deriving Show
 
-emptySourcePack = SourcePack [] 0 emptySource MI.empty
+emptySourcePack = SourcePack [] 0 emptySource mempty []
 
 mapSource :: (Source -> Source) -> SourcePack -> SourcePack
-mapSource f (SourcePack ss i s it) = SourcePack ss i (f s) it
+mapSource f sp = sp { source = f (source sp)}
 
 select :: SourcePack -> Selector -> SourcePack
-select (SourcePack ss i (Source _ _ _ ts) it) s@(STxt n) = SourcePack (s:ss) i (fromMaybe emptySource $  M.lookup n ts) it
-select (SourcePack ss i (Source _ _ is _) it) s@(SNum n) = SourcePack (s:ss) i (fromMaybe emptySource $ MI.lookup n is) it
+select sp n = sp { source = selectSource n (source sp), prefix = n : prefix sp}
 
 addErr' :: String -> SourcePack -> SourcePack
-addErr' e = mapSource (addErr e)
+addErr' e sp = sp {errs = e : errs sp}
 
-extractErr' :: SourcePack -> ([String], SourcePack)
-extractErr' (SourcePack ss i s it) = let (es, s') = extractErr s in (es, SourcePack ss i s' it)
+loadFile :: Reload -> SourcePack -> (Priority -> Source -> Writer [String] Source) -> SourcePack
+loadFile name SourcePack{..} go =
+  let (s', e) = runWriter $ go packId source
+  in SourcePack prefix (packId+1) s' (MI.insert packId name reEnv) (errs ++ e)
 
-loadFile :: Reload -> SourcePack -> (Priority -> Source -> Source) -> SourcePack
-loadFile name (SourcePack ss i s env) go = SourcePack ss (i+1) (go i s) $ MI.insert i name env
+tryLoadFile :: MonadIO m => (FilePath -> SourcePackT m ()) -> FilePath -> SourcePackT m ()
+tryLoadFile f file = do
+  b <- liftIO $ doesFileExist file
+  when b $ do
+    liftIO $ putStrLn $ "Load " <> file
+    f file
 
 load
-  :: (Functor f, Foldable f)
+  :: Foldable f
   => Reload
   -> f a
   -> (Priority -> a -> (Text, Value))
   -> SourcePack
   -> SourcePack
-load name fa f sp = loadFile name sp $ \i s -> foldl (go i) s fa
+load name fa f sp = loadFile name sp $ \i s -> foldl (go i) (return s) fa
   where
-    go i s a = let (k,v) = f i a in insert k v s
+    go i s a = s >>= \s' -> let (k,v) = f i a in insert k v s'
 
 loadMock :: Monad m => [(Text, Text)] -> SourcePackT m ()
-loadMock fs = modify $ load (emptyReload "") fs (\i (k,v) -> (k, VStr i v))
+loadMock fs = modify $ load (emptyReload "Mock") fs (\i (k,v) -> (k, VStr i v))
 
 type SourcePackT = StateT SourcePack
 
-runSourcePackT :: Monad m => SourcePackT m a -> m ([String], SourcePack)
-runSourcePackT ac = extractErr' <$> execStateT ac emptySourcePack
+runSourcePackT :: Monad m => SourcePackT m a -> m SourcePack
+runSourcePackT ac = execStateT ac emptySourcePack
 
diff --git a/src/Salak/Types/Selector.hs b/src/Salak/Types/Selector.hs
new file mode 100644
--- /dev/null
+++ b/src/Salak/Types/Selector.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Salak.Types.Selector where
+
+import           Control.Applicative  ((<|>))
+import           Data.Attoparsec.Text
+import           Data.List            (intercalate)
+import           Data.Text            (Text)
+import qualified Data.Text            as T
+
+data Selector
+  = SStr !Text
+  | SNum !Int
+  deriving (Eq, Ord)
+
+isStr :: Selector -> Bool
+isStr (SStr _) = True
+isStr _        = False
+
+instance Show Selector where
+  show (SStr x) = T.unpack x
+  show (SNum i) = "[" ++ show i ++ "]"
+
+toKey :: [Selector] -> String
+toKey = intercalate "." . go . reverse
+  where
+    go (a@(SStr _):cs) = let (b,c) = break isStr cs in (show a ++ concat (show <$> b)) : go c
+    go (a:cs)          = show a : go cs
+    go []              = []
+
+simpleSelectors :: Text -> [Selector]
+simpleSelectors as = fmap SStr $ filter (not.T.null) $ T.splitOn "." as
+
+selectors :: Text -> Either String [Selector]
+selectors = go . parse exprs . flip T.snoc '\n'
+  where
+    go (Done i r) = if i /= "\n" then Left $ "uncomplete parse" ++ T.unpack i else Right r
+    go a          = Left (show a)
+
+exprs :: Parser [Selector]
+exprs = concat <$> ( (expr <|> return []) `sepBy` char '.')
+
+-- xx
+-- xx.xx
+-- xx.xx[0]
+-- xx.xx[1].xx
+expr :: Parser [Selector]
+expr = do
+  name <- T.pack <$> do
+    a <- choice [letter, digit]
+    b <- many' (choice [letter, digit, char '-',  char '_'])
+    return (a:b)
+  ds   <- many' (paren decimal)
+  return $ SStr name : (SNum <$> ds)
+  where
+    paren e = do
+      _  <- char '['
+      ex <- e
+      _  <- char ']'
+      return ex
diff --git a/src/Salak/Types/Source.hs b/src/Salak/Types/Source.hs
new file mode 100644
--- /dev/null
+++ b/src/Salak/Types/Source.hs
@@ -0,0 +1,86 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE DeriveFunctor       #-}
+{-# LANGUAGE FlexibleInstances   #-}
+{-# LANGUAGE RecordWildCards     #-}
+{-# LANGUAGE TupleSections       #-}
+{-# LANGUAGE TypeFamilies        #-}
+module Salak.Types.Source where
+
+import           Control.Monad.Writer
+import qualified Data.Map.Strict      as M
+import           Data.Maybe
+import qualified Data.Text            as T
+import           Salak.Types.Selector
+import           Salak.Types.Value
+
+data SourceT v = Source
+  { value    :: v
+  , mapValue :: M.Map Selector (SourceT v)
+  } deriving (Eq, Functor)
+
+instance Foldable SourceT where
+  foldr g b Source{..} = M.foldl (foldr g) (g value b) mapValue
+
+type Source = SourceT QV
+
+showKey :: String -> Selector -> String
+showKey p (SStr k)
+  | null p    = T.unpack k
+  | otherwise = p <> "." <> T.unpack k
+showKey p (SNum k) = p <> "[" <> show k <> "]"
+
+instance Show Source where
+  show = unlines . go ""
+    where
+      go p Source{..} = concat $ M.foldrWithKey (\k v b -> go (showKey p k) v : b) [ g2 p value] mapValue
+      g2 x v = if nullQ v then [] else [x <> "=" <> show v]
+
+emptySource :: Source
+emptySource = Source mempty mempty
+
+foldSource :: (Value -> b -> b) -> b -> Source -> b
+foldSource f = foldr (\q b -> maybe b (`f` b) $ getQ q)
+
+sizeSource :: Source -> Int
+sizeSource = foldSource  (const (+1)) 0
+
+nullSource :: Source -> Bool
+nullSource = foldSource (\_ _ -> False) True
+
+selectSource :: Selector -> Source -> Source
+selectSource n Source{..} = fromMaybe emptySource $ M.lookup n mapValue
+
+updateSource :: Monad m => Selector -> (Source -> m Source) -> Source -> m Source
+updateSource n f ss = do
+  ss' <- f $ selectSource n ss
+  return $ ss { mapValue = M.insert n ss' (mapValue ss) }
+
+updateSources :: Monad m => [Selector] -> (Source -> m Source) -> Source -> m Source
+updateSources = flip (foldr updateSource)
+
+replace = replace' []
+
+replace' :: [Selector] -> Priority -> Source -> Source -> Writer [String] Source
+replace' ss i ns os = do
+  q' <- replaceQ (toKey ss) i (value ns) (value os)
+  m' <- mapM snd $ M.mapWithKey g2 $ M.unionWithKey go (f 0 $ mapValue ns) (f 1 $ mapValue os)
+  return (Source q' $ M.filter (not.nullSource) m')
+  where
+    f j m = (j :: Int,) . return <$> m
+    go k (_, a) (_, b) = (2, a >>= \a' -> b >>= \b' -> replace' (k:ss) i a' b')
+    g2 k (0, a) = (0, a >>= \a' -> replace' (k:ss) i a' emptySource)
+    g2 k (1, a) = (1, a >>= \a' -> replace' (k:ss) i emptySource a')
+    g2 _ (_, a) = (2 :: Int, a)
+
+insert :: T.Text -> Value -> Source -> Writer [String] Source
+insert k v s = case selectors k of
+  Left  e  -> tell [e] >> return s
+  Right k' -> return (insert' k' v s)
+
+insert' :: [Selector] -> Value -> Source -> Source
+insert' ns v = foldr go (insertSource v) ns
+  where
+    go n f s = s { mapValue = M.alter (Just . f . fromMaybe emptySource) n $ mapValue s}
+
+insertSource :: Value -> Source -> Source
+insertSource v s = s { value = insertQ v $ value s}
diff --git a/src/Salak/Types/Value.hs b/src/Salak/Types/Value.hs
new file mode 100644
--- /dev/null
+++ b/src/Salak/Types/Value.hs
@@ -0,0 +1,68 @@
+module Salak.Types.Value where
+
+import           Control.Monad.Writer
+import qualified Data.PQueue.Min      as Q
+import           Data.Scientific      (Scientific)
+import           Data.Text            (Text)
+import           Data.Time
+
+type Priority = Int
+
+data Value
+  = VStr   !Priority !Text
+  | VNum   !Priority !Scientific
+  | VBool  !Priority !Bool
+  | VZTime !Priority !TimeZone !LocalTime
+  | VLTime !Priority !LocalTime
+  | VDay   !Priority !Day
+  | VHour  !Priority !TimeOfDay
+  deriving Eq
+
+instance Ord Value where
+  compare a b = compare (getPriority a) (getPriority b)
+
+instance Show Value where
+  show v = let (a,b,c) = typeOfV v in c <> ":" <> b <> "#" <> show a
+
+typeOfV :: Value -> (Priority, String, String)
+typeOfV (VStr   a b)   = (a, "Str",       show b)
+typeOfV (VNum   a b)   = (a, "Num",       show b)
+typeOfV (VBool  a b)   = (a, "Bool",      show b)
+typeOfV (VZTime a b c) = (a, "ZonedTime", show (ZonedTime c b))
+typeOfV (VLTime a b)   = (a, "LocalTime", show b)
+typeOfV (VDay   a b)   = (a, "Day",       show b)
+typeOfV (VHour  a b)   = (a, "TimeOfDay", show b)
+
+getPriority :: Value -> Priority
+getPriority x = let (a,_,_) = typeOfV x in a
+
+getType :: Value -> String
+getType x = let (_,b,_) = typeOfV x in b
+
+getV :: Value -> String
+getV x = let (_,_,b) = typeOfV x in b
+
+type QV = Q.MinQueue Value
+
+getQ :: QV -> Maybe Value
+getQ = Q.getMin
+
+nullQ :: QV -> Bool
+nullQ = Q.null
+
+insertQ :: Value -> QV -> QV
+insertQ = Q.insert
+
+replaceQ :: Monad m => String -> Priority -> QV -> QV -> WriterT [String] m QV
+replaceQ s i nq q = do
+  let (a,b) = Q.partition ((==i) . getPriority) q
+      go v  = tell $ (\vi -> "#" <> show i <> " " <> vi) <$> v
+  if a == nq
+    then return q
+    else case getQ nq of
+      Just v -> do
+        go [(if Q.null a then "Add " else "Mod ") ++ s]
+        return $ Q.insert v b
+      _      -> do
+        unless (Q.null a) $ go ["Del " ++ s]
+        return b
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -6,13 +6,18 @@
 
 module Main where
 
+import           Control.Monad.Reader
+import           Control.Monad.Writer
 import           Data.Either
-import           Data.List       (intercalate)
-import           Data.Text       (Text, pack, unpack)
+import           Data.List            (intercalate)
+import           Data.Menshen
+import           Data.Text            (Text, pack, unpack)
 import           GHC.Generics
 import           Salak
 import           Salak.Prop
 import           Salak.Types
+import           Salak.Types.Selector
+import           Salak.Types.Source
 import           Test.Hspec
 import           Test.QuickCheck
 
@@ -47,55 +52,66 @@
   { hello :: String } deriving (Eq, Show, Generic)
 
 instance FromProp SubConf where
-  fromProp = SubConf <$> "hello" .?= "yyy"
+  fromProp = SubConf <$> "hello" .?= "yyy" ? pattern "[a-z]{3,16}"
 
 instance FromProp Conf
 
 specProperty = do
   context "selectors" $ do
     it "normal" $ do
-      selectors ""      `shouldBe` Right []
-      selectors "."     `shouldBe` Right []
-      selectors ".."    `shouldBe` Right []
-      selectors "xx"    `shouldBe` Right [STxt "xx"]
-      selectors "xx[0]" `shouldBe` Right [STxt "xx", SNum 0]
-      selectors "xx.yy" `shouldBe` Right [STxt "xx", STxt "yy"]
+      selectors ""         `shouldBe` Right []
+      selectors "."        `shouldBe` Right []
+      selectors ".."       `shouldBe` Right []
+      selectors "xx"       `shouldBe` Right [SStr "xx"]
+      selectors "xx[0]"    `shouldBe` Right [SStr "xx", SNum 0]
+      selectors "xx.yy"    `shouldBe` Right [SStr "xx", SStr "yy"]
+      selectors "xx[0][1]" `shouldBe` Right [SStr "xx", SNum 0, SNum 1]
+      toKey (reverse $ SStr "x" : (SNum <$> [0..9])) `shouldBe` "x[0][1][2][3][4][5][6][7][8][9]"
     it "QuickCheck" $ do
       quickCheck $ \s -> let s' = unKey s in (toKey . reverse <$> selectors s') `shouldBe` Right (unpack s')
   context "source" $ do
     it "normal" $ do
-      nullSource emptySource `shouldBe` True
-      sizeSouce  emptySource `shouldBe` 0
+      sizeSource emptySource `shouldBe` 0
     it "normal - 2" $ do
-      let s1 = insert "hello" (VStr 0 "world") emptySource
-      sizeSouce s1           `shouldBe` 1
-      let s2 = insert "1"     (VStr 0 "world") emptySource
-      sizeSouce s2           `shouldBe` 1
-      let (es,_) = extractErr s2
-      length es              `shouldBe` 0
-    it "merge" $ do
-      let s  = insert "hello" (VStr 0 "world") emptySource
-          s1 = insert "hello" (VStr 0 "xxxxx") emptySource
-          (e2, s2) = extractErr $ replace 0 emptySource s
+      let (s1,e1) = runWriter $ insert "hello" (VStr 0 "world") emptySource
+      sizeSource s1       `shouldBe` 1
+      length e1           `shouldBe` 0
+    it "normal - 3" $ do
+      let (s2,e2) = runWriter $ insert "1"     (VStr 0 "world") emptySource
+      sizeSource s2       `shouldBe` 1
+      length e2           `shouldBe` 00
+    it "normal - 4" $ do
+      let (s3,e3) = runWriter $ insert "a.b"   (VStr 0 "world") emptySource
+      print s3
+      sizeSource s3       `shouldBe` 1
+      length e3           `shouldBe` 0
+  context "source - merge" $ do
+    let s  = fst $ runWriter $ insert "hello" (VStr 0 "world") emptySource
+        s1 = fst $ runWriter $ insert "hello" (VStr 0 "xxxxx") emptySource
+    it "merge - del" $ do
+      let (s2,e2) = runWriter $ replace 0 emptySource s
       s2 `shouldBe` emptySource
-      e2 `shouldBe` ["Del hello"]
-      let (e3, s3) = extractErr $ replace 0 s1 s
-      e3 `shouldBe` ["Mod hello"]
+      e2 `shouldBe` ["#0 Del hello"]
+    it "merge - mod" $ do
+      let (s3,e3) = runWriter $ replace 0 s1 s
+      e3 `shouldBe` ["#0 Mod hello"]
       s3 `shouldBe` s1
-      let (e4, s4) = extractErr $ replace 0 s emptySource
-      e4 `shouldBe` ["Add hello"]
+    it "merge - add" $ do
+      let (s4,e4) = runWriter $ replace 0 s emptySource
+      e4 `shouldBe` ["#0 Add hello"]
       s4 `shouldBe` s
-      let (e5, s5) = extractErr $ replace 0 s s
+    it "merge - unchange" $ do
+      let (s5,e5) = runWriter $ replace 0 s s
       e5 `shouldBe` []
       s5 `shouldBe` s
   context "Generic" $ do
     it "conf" $ do
-      (e, sp) <- runSourcePackT $ loadMock
+      sp <- runSourcePackT $ loadMock
         [ ("name", "Daniel")
         , ("age", "18")
         , ("male", "yes")
         ]
-      e `shouldBe` []
+      errs sp `shouldBe` []
       let a = search "" sp :: Either String Conf
       print a
       isRight a `shouldBe` True
diff --git a/test/salak.yml b/test/salak.yml
deleted file mode 100644
--- a/test/salak.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-array:
-  - a
-  - b
-hello:
-  world
-
-config:
-  name: hello
-  dir: /root
