diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015 Ting-Yen Lai
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# edis
+
+Statically typechecked Redis
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/edis.cabal b/edis.cabal
new file mode 100644
--- /dev/null
+++ b/edis.cabal
@@ -0,0 +1,46 @@
+name:                   edis
+version:                0.0.1.0
+synopsis:
+    Statically typechecked client for Redis
+description:
+    This library is a Haskell client for the Redis datastore built on top of
+    Hedis, with stronger type-level guarantees.
+    .
+    For detailed documentation, see the "Database.Edis" module.
+    .
+license:                MIT
+license-file:           LICENSE
+author:                 Ting-Yan Lai
+maintainer:             banacorn@gmail.com
+copyright:              Copyright (c) 2015 Ting-Yan Lai
+category:               Database
+build-type:             Simple
+extra-source-files:     README.md
+cabal-version:          >=1.10
+
+source-repository head
+    type:     git
+    location: https://github.com/banacorn/edis
+
+library
+    exposed-modules:    Database.Edis
+    other-modules:      Database.Edis.Type
+                    ,   Database.Edis.Helper
+                    ,   Database.Edis.Command
+                    ,   Database.Edis.Command.Key
+                    ,   Database.Edis.Command.String
+                    ,   Database.Edis.Command.List
+                    ,   Database.Edis.Command.Hash
+                    ,   Database.Edis.Command.Set
+                    ,   Database.Edis.Command.ZSet
+                    ,   Database.Edis.Command.Server
+                    ,   Database.Edis.Command.Connection
+                    ,   Database.Edis.Command.Scripting
+                    ,   Database.Edis.Command.PubSub
+    build-depends:      base        >=4.8 && <5
+                    ,   bytestring  >=0.10
+                    ,   hedis       >=0.6
+                    ,   cereal      >=0.4
+    hs-source-dirs:     src/
+    default-language:   Haskell2010
+    GHC-Options: -Wall
diff --git a/src/Database/Edis.hs b/src/Database/Edis.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis.hs
@@ -0,0 +1,52 @@
+module Database.Edis (
+        -- * How to use Edis
+        -- |It's basically the same as in <https://hackage.haskell.org/package/hedis Hedis>.
+        --  Most of the entities in Hedis are re-exported, or have the same names
+        --  with types.
+        --
+        -- @
+        -- -- connects to localhost:6379
+        -- conn <- 'connect' 'defaultConnectInfo'
+        -- @
+        --
+        -- Send commands to the server:
+        --
+        -- @
+        -- {-\# LANGUAGE OverloadedStrings \#-}
+        -- ...
+        -- 'runRedis' conn $ 'unEdis' $ 'start'
+        --      '`bind`' \_ -> 'set'     ('Proxy' :: 'Proxy' \"hello\")    True
+        --      '`bind`' \_ -> 'set'     ('Proxy' :: 'Proxy' \"world\")    [True, False]
+        --      '`bind`' \_ -> 'get'     ('Proxy' :: 'Proxy' \"world\")
+        -- @
+        --
+        -- Unfortunately 'Edis' is not a monad, so we can't use do-notations in
+        -- the program. But the example above can be rewritten with '>>>' like
+        -- this, if no variable bindings and result passings are needed.
+        --
+        -- @
+        -- {-\# LANGUAGE OverloadedStrings \#-}
+        -- ...
+        -- 'runRedis' conn $ 'unEdis' $ 'start'
+        --      '>>>' 'set'     ('Proxy' :: 'Proxy' \"hello\")    True
+        --      '>>>' 'set'     ('Proxy' :: 'Proxy' \"world\")    [True, False]
+        --      '>>>' 'get'     ('Proxy' :: 'Proxy' \"world\")
+        -- @
+        --
+        -- * Commands
+        module Database.Edis.Command
+    ,   Proxy(..)
+    ,   Edis(..)
+    ,   IMonad(..)
+    ,   (>>>)
+
+    ,   Redis.Reply
+    ,   Redis.runRedis
+    ,   Redis.connect
+    ,   Redis.defaultConnectInfo
+    ) where
+
+import           Data.Proxy
+import qualified Database.Redis as Redis
+import           Database.Edis.Type
+import           Database.Edis.Command
diff --git a/src/Database/Edis/Command.hs b/src/Database/Edis/Command.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command.hs
@@ -0,0 +1,60 @@
+{-# LANGUAGE DataKinds, TypeFamilies, TypeOperators, OverloadedStrings, PolyKinds #-}
+
+module Database.Edis.Command (
+        -- ** Keys
+        module Database.Edis.Command.Key
+        -- ** Strings
+    ,   module Database.Edis.Command.String
+        -- ** Lists
+    ,   module Database.Edis.Command.List
+        -- ** Hashes
+    ,   module Database.Edis.Command.Hash
+        -- ** Sets
+    ,   module Database.Edis.Command.Set
+        -- ** Sorted Sets
+    ,   module Database.Edis.Command.ZSet
+        -- ** Server
+    ,   module Database.Edis.Command.Server
+        -- ** Connection
+    ,   module Database.Edis.Command.Connection
+        -- ** Scripting
+    ,   module Database.Edis.Command.Scripting
+        -- ** Pub/Sub
+    ,   module Database.Edis.Command.PubSub
+        -- ** Assertions on types
+    ,   start, declare, renounce
+    ) where
+
+import Database.Edis.Type
+import Database.Edis.Command.Key
+import Database.Edis.Command.String
+import Database.Edis.Command.List
+import Database.Edis.Command.Hash
+import Database.Edis.Command.Set
+import Database.Edis.Command.ZSet
+import Database.Edis.Command.Server
+import Database.Edis.Command.Connection
+import Database.Edis.Command.Scripting
+import Database.Edis.Command.PubSub
+
+import Data.Proxy               (Proxy)
+import GHC.TypeLits
+
+--------------------------------------------------------------------------------
+--  Helper functions
+--------------------------------------------------------------------------------
+
+start :: Edis '[] '[] ()
+start = Edis (return ())
+
+--------------------------------------------------------------------------------
+--  Declaration
+--------------------------------------------------------------------------------
+
+declare :: (KnownSymbol s, Member xs s ~ 'False)
+        => Proxy s -> Proxy x -> Edis xs (Set xs s x) ()
+declare _ _ = Edis $ return ()
+
+renounce :: (KnownSymbol s, Member xs s ~ 'True)
+        => Proxy s -> Edis xs (Del xs s) ()
+renounce _ = Edis $ return ()
diff --git a/src/Database/Edis/Command/Connection.hs b/src/Database/Edis/Command/Connection.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/Connection.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE PolyKinds #-}
+
+module Database.Edis.Command.Connection where
+
+import Database.Edis.Type
+
+import Data.ByteString          (ByteString)
+import Database.Redis as Redis hiding (decode)
+
+--------------------------------------------------------------------------------
+--  Connection
+--------------------------------------------------------------------------------
+
+auth :: ByteString -> Edis xs xs (Either Reply Status)
+auth = Edis . Redis.auth
+
+echo :: ByteString -> Edis xs xs (Either Reply ByteString)
+echo = Edis . Redis.echo
+
+ping :: Edis xs xs (Either Reply Status)
+ping = Edis Redis.ping
+
+quit :: Edis xs xs (Either Reply Status)
+quit = Edis Redis.quit
+
+select :: Integer -> Edis xs xs (Either Reply Status)
+select = Edis . Redis.select
diff --git a/src/Database/Edis/Command/Hash.hs b/src/Database/Edis/Command/Hash.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/Hash.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeOperators, OverloadedStrings #-}
+
+module Database.Edis.Command.Hash where
+
+import Database.Edis.Type
+import Database.Edis.Helper
+
+import Data.ByteString          (ByteString)
+import Data.Proxy               (Proxy)
+import Data.Serialize           (Serialize, encode)
+import Data.Type.Bool
+import Database.Redis as Redis hiding (decode)
+import GHC.TypeLits
+
+--------------------------------------------------------------------------------
+--  Hashes
+--------------------------------------------------------------------------------
+
+hdel :: (KnownSymbol k, KnownSymbol f, HashOrNX xs k)
+        => Proxy k -> Proxy f
+        -> Edis xs (DelHash xs k f) (Either Reply Integer)
+hdel key field = Edis $ Redis.hdel (encodeKey key) [encodeKey field]
+
+hexists :: (KnownSymbol k, KnownSymbol f, HashOrNX xs k)
+        => Proxy k -> Proxy f
+        -> Edis xs xs (Either Reply Bool)
+hexists key field = Edis $ Redis.hexists (encodeKey key) (encodeKey field)
+
+hget :: (KnownSymbol k, KnownSymbol f, Serialize x
+        , 'Just (StringOf x) ~ GetHash xs k f)
+        => Proxy k -> Proxy f
+        -> Edis xs xs (Either Reply (Maybe x))
+hget key field = Edis $ Redis.hget (encodeKey key) (encodeKey field) >>= decodeAsMaybe
+
+hincrby :: (KnownSymbol k, KnownSymbol f, HashOrNX xs k)
+        => Proxy k -> Proxy f -> Integer
+        -> Edis xs (SetHash xs k f Integer) (Either Reply Integer)
+hincrby key field n = Edis $ Redis.hincrby (encodeKey key) (encodeKey field) n
+
+hincrbyfloat :: (KnownSymbol k, KnownSymbol f, HashOrNX xs k)
+        => Proxy k -> Proxy f -> Double
+        -> Edis xs (SetHash xs k f Double) (Either Reply Double)
+hincrbyfloat key field n = Edis $ Redis.hincrbyfloat (encodeKey key) (encodeKey field) n
+
+hkeys :: (KnownSymbol k, HashOrNX xs k)
+        => Proxy k
+        -> Edis xs xs (Either Reply [ByteString])
+hkeys key = Edis $ Redis.hkeys (encodeKey key)
+
+hlen :: (KnownSymbol k, HashOrNX xs k)
+        => Proxy k
+        -> Edis xs xs (Either Reply Integer)
+hlen key = Edis $ Redis.hlen (encodeKey key)
+
+hset :: (KnownSymbol k, KnownSymbol f, Serialize x, HashOrNX xs k)
+        => Proxy k -> Proxy f -> x
+        -> Edis xs (SetHash xs k f (StringOf x)) (Either Reply Bool)
+hset key field val = Edis $ Redis.hset (encodeKey key) (encodeKey field) (encode val)
+
+hsetnx :: (KnownSymbol k, KnownSymbol f, Serialize x, HashOrNX xs k)
+        => Proxy k -> Proxy f -> x
+        -> Edis xs (If (MemHash xs k f) xs (SetHash xs k f (StringOf x))) (Either Reply Bool)
+hsetnx key field val = Edis $ Redis.hsetnx (encodeKey key) (encodeKey field) (encode val)
diff --git a/src/Database/Edis/Command/Key.hs b/src/Database/Edis/Command/Key.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/Key.hs
@@ -0,0 +1,104 @@
+{-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeOperators, OverloadedStrings #-}
+
+module Database.Edis.Command.Key where
+
+import Database.Edis.Type
+import Database.Edis.Helper
+
+import Data.ByteString          (ByteString)
+import Data.Proxy               (Proxy)
+import Data.Serialize           (Serialize)
+import Data.Type.Bool
+import Data.Type.Equality
+import Database.Redis as Redis hiding (decode)
+import GHC.TypeLits
+
+
+--------------------------------------------------------------------------------
+--  Keys
+--------------------------------------------------------------------------------
+
+del :: KnownSymbol s => Proxy s -> Edis xs (Del xs s) (Either Reply Integer)
+del key = Edis $ Redis.del [encodeKey key]
+
+dump :: ByteString -> Edis xs xs (Either Reply ByteString)
+dump key = Edis $ Redis.dump key
+
+exists :: ByteString -> Edis xs xs (Either Reply Bool)
+exists key = Edis $ Redis.exists key
+
+expire :: ByteString -> Integer -> Edis xs xs (Either Reply Bool)
+expire key n = Edis $ Redis.expire key n
+
+expireat :: ByteString -> Integer -> Edis xs xs (Either Reply Bool)
+expireat key n = Edis $ Redis.expireat key n
+
+keys :: ByteString -> Edis xs xs (Either Reply [ByteString])
+keys pattern = Edis $ Redis.keys pattern
+
+migrate :: ByteString -> ByteString -> ByteString -> Integer -> Integer -> Edis xs xs (Either Reply Status)
+migrate host port key destinationDb timeout = Edis $ Redis.migrate host port key destinationDb timeout
+
+move :: ByteString -> Integer -> Edis xs xs (Either Reply Bool)
+move key db = Edis $ Redis.move key db
+
+objectRefcount :: ByteString -> Edis xs xs (Either Reply Integer)
+objectRefcount key = Edis $ Redis.objectRefcount key
+
+objectEncoding :: ByteString -> Edis xs xs (Either Reply ByteString)
+objectEncoding key = Edis $ Redis.objectEncoding key
+
+objectIdletime :: ByteString -> Edis xs xs (Either Reply Integer)
+objectIdletime key = Edis $ Redis.objectIdletime key
+
+persist :: ByteString -> Edis xs xs (Either Reply Bool)
+persist key = Edis $ Redis.persist key
+
+pexpire :: ByteString -> Integer -> Edis xs xs (Either Reply Bool)
+pexpire key n = Edis $ Redis.pexpire key n
+
+pexpireat :: ByteString -> Integer -> Edis xs xs (Either Reply Bool)
+pexpireat key n = Edis $ Redis.pexpireat key n
+
+pttl :: ByteString -> Edis xs xs (Either Reply Integer)
+pttl key = Edis $ Redis.pttl key
+
+randomkey :: Edis xs xs (Either Reply (Maybe ByteString))
+randomkey = Edis $ Redis.randomkey
+
+-- 1. the old key must exist
+-- 2. the old key and the new key must be different
+rename :: (KnownSymbol s, KnownSymbol t, Member xs s ~ 'True, Get xs s ~ 'Just x, (s == t) ~ 'False)
+        => Proxy s -> Proxy t -> Edis xs (Set (Del xs s) t x) (Either Reply Status)
+rename key key' = Edis $ Redis.rename (encodeKey key) (encodeKey key')
+
+
+
+-- 1. the old key must exist
+-- 2. the old key and the new key must be different
+-- 3. if the new key exists
+--      then nothing happens
+--      else the new key is removed
+renamenx :: (KnownSymbol s, KnownSymbol t, Member xs s ~ 'True, Get xs s ~ 'Just x, (s == t) ~ 'False)
+        => Proxy s -> Proxy t -> Edis xs (If (Member xs t) xs (Del xs s)) (Either Reply Bool)
+renamenx key key' = Edis $ Redis.renamenx (encodeKey key) (encodeKey key')
+
+restore :: ByteString -> Integer -> ByteString -> Edis xs xs (Either Reply Status)
+restore key n val = Edis $ Redis.restore key n val
+
+-- must be a List, a Set or a Sorted Set
+sort :: (KnownSymbol s, Serialize x, Member xs s ~ 'True
+        , (Get xs s == 'Just (ListOf x) || Get xs s == 'Just (SetOf x) || Get xs s == 'Just (ZSetOf x)) ~ 'True)
+        => Proxy s -> SortOpts -> Edis xs xs (Either Reply [x])
+sort key opt = Edis $ Redis.sort (encodeKey key) opt >>= decodeAsList
+
+sortStore :: (KnownSymbol s, KnownSymbol t, Member xs s ~ 'True, FromJust (Get xs s) ~ x
+        , (IsList (FromJust (Get xs s)) || IsSet (FromJust (Get xs s)) || IsZSet (FromJust (Get xs s))) ~ 'True)
+        => Proxy s -> Proxy t -> SortOpts -> Edis xs (Set xs s x) (Either Reply Integer)
+sortStore key dest opt = Edis $ Redis.sortStore (encodeKey key) (encodeKey dest) opt
+
+ttl :: ByteString -> Edis xs xs (Either Reply Integer)
+ttl key = Edis $ Redis.ttl key
+
+getType :: ByteString -> Edis xs xs (Either Reply RedisType)
+getType key = Edis $ Redis.getType key
diff --git a/src/Database/Edis/Command/List.hs b/src/Database/Edis/Command/List.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/List.hs
@@ -0,0 +1,115 @@
+{-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeOperators, OverloadedStrings #-}
+
+module Database.Edis.Command.List where
+
+import Database.Edis.Type
+import Database.Edis.Helper
+
+import Data.ByteString          (ByteString)
+import Data.Proxy               (Proxy)
+import Data.Serialize           (Serialize, encode)
+import Data.Type.Bool
+import Database.Redis as Redis hiding (decode)
+import GHC.TypeLits
+
+
+--------------------------------------------------------------------------------
+--  List
+--------------------------------------------------------------------------------
+
+blpop :: (KnownSymbol s, Serialize x, ListOf x ~ FromJust (Get xs s))
+        => Proxy s -> Integer
+        -> Edis xs xs (Either Reply (Maybe (ByteString, x)))
+blpop key timeout = Edis $ Redis.blpop [encodeKey key] timeout >>= decodeBPOP
+
+brpop :: (KnownSymbol s, Serialize x, ListOf x ~ FromJust (Get xs s))
+        => Proxy s -> Integer
+        -> Edis xs xs (Either Reply (Maybe (ByteString, x)))
+brpop key timeout = Edis $ Redis.brpop [encodeKey key] timeout >>= decodeBPOP
+
+brpoplpush :: (KnownSymbol s, KnownSymbol t, Serialize x
+        , ListOf x ~ FromJust (Get xs s), ListOrNX xs s)
+        => Proxy s -> Proxy t -> Integer
+        -> Edis xs (Set xs s (ListOf x)) (Either Reply (Maybe x))
+brpoplpush key dest timeout = Edis $ Redis.brpoplpush (encodeKey key) (encodeKey dest) timeout >>= decodeAsMaybe
+
+lindex :: (KnownSymbol s, Serialize x, ListOf x ~ FromJust (Get xs s))
+        => Proxy s -> Integer
+        -> Edis xs xs (Either Reply (Maybe x))
+lindex key index = Edis $ Redis.lindex (encodeKey key) index >>= decodeAsMaybe
+
+linsertBefore :: (KnownSymbol s, Serialize x, ListOrNX xs s)
+        => Proxy s -> x -> x
+        -> Edis xs xs (Either Reply Integer)
+linsertBefore key pivot val = Edis $ Redis.linsertBefore (encodeKey key) (encode pivot) (encode val)
+
+linsertAfter :: (KnownSymbol s, Serialize x, ListOrNX xs s)
+        => Proxy s -> x -> x
+        -> Edis xs xs (Either Reply Integer)
+linsertAfter key pivot val = Edis $ Redis.linsertAfter (encodeKey key) (encode pivot) (encode val)
+
+llen :: (KnownSymbol s, ListOrNX xs s)
+        => Proxy s
+        -> Edis xs xs (Either Reply Integer)
+llen key = Edis $ Redis.llen (encodeKey key)
+
+lpop :: (KnownSymbol s, Serialize x, 'Just (ListOf x) ~ Get xs s)
+        => Proxy s
+        -> Edis xs xs (Either Reply (Maybe x))
+lpop key = Edis $ Redis.lpop (encodeKey key) >>= decodeAsMaybe
+
+lpush :: (KnownSymbol s, Serialize x, ListOrNX xs s)
+      => Proxy s -> x
+      -> Edis xs (Set xs s (ListOf x)) (Either Reply Integer)
+lpush key val = Edis $ Redis.lpush (encodeKey key) [encode val]
+
+-- no operation will be performed when key does not yet exist.
+lpushx :: (KnownSymbol s, Serialize x, 'Just (ListOf x) ~ Get xs s)
+        => Proxy s -> x
+        -> Edis xs xs (Either Reply Integer)
+lpushx key val = Edis $ Redis.lpushx (encodeKey key) (encode val)
+
+lrange :: (KnownSymbol s, Serialize x, ListOrNX xs s)
+     => Proxy s -> Integer -> Integer
+     -> Edis xs xs (Either Reply [x])
+lrange key from to = Edis $ Redis.lrange (encodeKey key) from to >>= decodeAsList
+
+lrem :: (KnownSymbol s, Serialize x, ListOrNX xs s)
+      => Proxy s -> Integer -> x
+      -> Edis xs xs (Either Reply Integer)
+lrem key count val = Edis $ Redis.lrem (encodeKey key) count (encode val)
+
+lset :: (KnownSymbol s, Serialize x, IsList (FromJust (Get xs s)) ~ 'True)
+      => Proxy s -> Integer -> x
+      -> Edis xs xs (Either Reply Status)
+lset key index val = Edis $ Redis.lset (encodeKey key) index (encode val)
+
+ltrim :: (KnownSymbol s, ListOrNX xs s)
+      =>  Proxy s -> Integer -> Integer
+      -> Edis xs xs (Either Reply Status)
+ltrim key from to = Edis $ Redis.ltrim (encodeKey key) from to
+
+rpop :: (KnownSymbol s, Serialize x, 'Just (ListOf x) ~ Get xs s)
+        => Proxy s
+        -> Edis xs xs (Either Reply (Maybe x))
+rpop key = Edis $ Redis.rpop (encodeKey key) >>= decodeAsMaybe
+
+-- 1. source and target must be List or does not yet exists
+rpoplpush :: (KnownSymbol s, KnownSymbol t, Serialize x
+        , ListOrNX xs s , ListOrNX xs t)
+        => Proxy s -> Proxy t
+        -> Edis xs (If (IsList (FromJust (Get xs s)))
+                    (Set xs s (ListOf x))
+                    xs) (Either Reply (Maybe x))
+rpoplpush key dest = Edis $ Redis.rpoplpush (encodeKey key) (encodeKey dest) >>= decodeAsMaybe
+
+rpush :: (KnownSymbol s, Serialize x, ListOrNX xs s)
+      => Proxy s -> x
+      -> Edis xs (Set xs s (ListOf x)) (Either Reply Integer)
+rpush key val = Edis $ Redis.rpush (encodeKey key) [encode val]
+
+-- no operation will be performed when key does not yet exist.
+rpushx :: (KnownSymbol s, Serialize x, 'Just (ListOf x) ~ Get xs s)
+        => Proxy s -> x
+        -> Edis xs xs (Either Reply Integer)
+rpushx key val = Edis $ Redis.rpushx (encodeKey key) (encode val)
diff --git a/src/Database/Edis/Command/PubSub.hs b/src/Database/Edis/Command/PubSub.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/PubSub.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE PolyKinds #-}
+
+module Database.Edis.Command.PubSub (
+        Database.Edis.Command.PubSub.publish
+    ,   Database.Edis.Command.PubSub.pubSub
+    ,   subscribe
+    ,   unsubscribe
+    ,   psubscribe
+    ,   punsubscribe
+) where
+
+import Database.Edis.Type
+
+import Data.ByteString          (ByteString)
+import Database.Redis as Redis hiding (decode)
+
+--------------------------------------------------------------------------------
+--  Pub/Sub
+--------------------------------------------------------------------------------
+
+publish :: ByteString -> ByteString -> Edis xs xs (Either Reply Integer)
+publish channel message = Edis $ Redis.publish channel message
+
+pubSub :: PubSub -> (Message -> IO PubSub) -> Edis xs xs ()
+pubSub initSub callback = Edis $ Redis.pubSub initSub callback
diff --git a/src/Database/Edis/Command/Scripting.hs b/src/Database/Edis/Command/Scripting.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/Scripting.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE PolyKinds #-}
+
+module Database.Edis.Command.Scripting where
+
+import Database.Edis.Type
+
+import Data.ByteString          (ByteString)
+import Database.Redis as Redis hiding (decode)
+
+--------------------------------------------------------------------------------
+--  Scripting
+--------------------------------------------------------------------------------
+
+eval :: (RedisResult a)
+     => ByteString -> [ByteString] -> [ByteString] -> Edis xs xs (Either Reply a)
+eval script keys' args = Edis $ Redis.eval script keys' args
+
+evalsha :: (RedisResult a)
+     => ByteString -> [ByteString] -> [ByteString] -> Edis xs xs (Either Reply a)
+evalsha script keys' args = Edis $ Redis.evalsha script keys' args
+
+scriptExists :: [ByteString] -> Edis xs xs (Either Reply [Bool])
+scriptExists scripts = Edis $ Redis.scriptExists scripts
+
+scriptFlush :: Edis xs xs (Either Reply Status)
+scriptFlush = Edis $ Redis.scriptFlush
+
+scriptKill :: Edis xs xs (Either Reply Status)
+scriptKill = Edis $ Redis.scriptFlush
+
+scriptLoad :: ByteString -> Edis xs xs (Either Reply ByteString)
+scriptLoad script = Edis $ Redis.scriptLoad script
diff --git a/src/Database/Edis/Command/Server.hs b/src/Database/Edis/Command/Server.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/Server.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE PolyKinds #-}
+
+module Database.Edis.Command.Server where
+
+import Database.Edis.Type
+
+import Data.ByteString          (ByteString)
+import Database.Redis as Redis hiding (decode)
+
+--------------------------------------------------------------------------------
+--  Server
+--------------------------------------------------------------------------------
+
+bgrewriteaof :: Edis xs xs (Either Reply Status)
+bgrewriteaof = Edis $ Redis.bgrewriteaof
+
+bgsave :: Edis xs xs (Either Reply Status)
+bgsave = Edis $ Redis.bgsave
+
+configGet :: ByteString -> Edis xs xs (Either Reply [(ByteString, ByteString)])
+configGet param = Edis $ Redis.configGet param
+
+configResetstat :: Edis xs xs (Either Reply Status)
+configResetstat = Edis $ Redis.configResetstat
+
+configSet :: ByteString -> ByteString -> Edis xs xs (Either Reply Status)
+configSet param val = Edis $ Redis.configSet param val
+
+dbsize :: Edis xs xs (Either Reply Integer)
+dbsize = Edis $ Redis.dbsize
+
+debugObject :: ByteString -> Edis xs xs (Either Reply ByteString)
+debugObject key = Edis $ Redis.debugObject key
+
+flushall :: Edis xs xs (Either Reply Status)
+flushall = Edis $ Redis.flushall
+
+flushdb :: Edis xs xs (Either Reply Status)
+flushdb = Edis $ Redis.flushdb
+
+info :: Edis xs xs (Either Reply ByteString)
+info = Edis $ Redis.info
+
+lastsave :: Edis xs xs (Either Reply Integer)
+lastsave = Edis $ Redis.lastsave
+
+save :: Edis xs xs (Either Reply Status)
+save = Edis $ Redis.save
+
+slaveof :: ByteString -> ByteString -> Edis xs xs (Either Reply Status)
+slaveof host port = Edis $ Redis.slaveof host port
+
+slowlogGet :: Integer -> Edis xs xs (Either Reply [Slowlog])
+slowlogGet count = Edis $ Redis.slowlogGet count
+
+slowlogLen :: Edis xs xs (Either Reply Integer)
+slowlogLen = Edis $ Redis.slowlogLen
+
+slowlogReset :: Edis xs xs (Either Reply Status)
+slowlogReset = Edis $ Redis.slowlogReset
+
+time :: Edis xs xs (Either Reply (Integer, Integer))
+time = Edis $ Redis.time
diff --git a/src/Database/Edis/Command/Set.hs b/src/Database/Edis/Command/Set.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/Set.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeOperators, OverloadedStrings #-}
+
+module Database.Edis.Command.Set where
+
+import Database.Edis.Type
+import Database.Edis.Helper
+
+import Data.Proxy               (Proxy)
+import Data.Serialize           (Serialize, encode)
+import Data.Type.Bool
+import Database.Redis as Redis hiding (decode)
+import GHC.TypeLits
+
+--------------------------------------------------------------------------------
+--  Sets
+--------------------------------------------------------------------------------
+
+sadd :: (KnownSymbol s, Serialize x, SetOrNX xs s)
+        => Proxy s -> x
+        -> Edis xs (If (Member xs s) xs (Set xs s (SetOf x))) (Either Reply Integer)
+sadd key val = Edis $ Redis.sadd (encodeKey key) [encode val]
+
+scard :: (KnownSymbol s, SetOrNX xs s)
+        => Proxy s
+        -> Edis xs xs (Either Reply Integer)
+scard key = Edis $ Redis.scard (encodeKey key)
+
+sdiff :: (KnownSymbol s, KnownSymbol t, Serialize x
+        , 'Just (SetOf x) ~ Get xs s  -- must exist
+        , SetOrNX xs s)
+        => Proxy s -> Proxy t
+        -> Edis xs xs (Either Reply [x])
+sdiff key key' = Edis $ Redis.sdiff [encodeKey key, encodeKey key'] >>= decodeAsList
+
+sdiffstore :: (KnownSymbol s, KnownSymbol t, SetOrNX xs s, SetOrNX xs t)
+        => Proxy s -> Proxy t
+        -> Edis xs xs (Either Reply Integer)
+sdiffstore dest key = Edis $ Redis.sdiffstore (encodeKey dest) [encodeKey key]
+
+sinter :: (KnownSymbol s, KnownSymbol t, Serialize x
+        , 'Just (SetOf x) ~ Get xs s  -- must exist
+        , SetOrNX xs s)
+        => Proxy s -> Proxy t
+        -> Edis xs xs (Either Reply [x])
+sinter key key' = Edis $ Redis.sinter [encodeKey key, encodeKey key'] >>= decodeAsList
+
+sinterstore :: (KnownSymbol s, KnownSymbol t, SetOrNX xs s, SetOrNX xs t)
+        => Proxy s -> Proxy t
+        -> Edis xs xs (Either Reply Integer)
+sinterstore dest key = Edis $ Redis.sinterstore (encodeKey dest) [encodeKey key]
+
+smembers :: (KnownSymbol s, Serialize x
+          , 'Just (SetOf x) ~ Get xs s)
+         => Proxy s
+         -> Edis xs xs  (Either Reply [x])
+smembers key = Edis $ Redis.smembers (encodeKey key) >>= decodeAsList
+
+--  if the `dest` is not a set then the `src` must not exist
+--  else the `src` and `dest` may be a set or does not exist.
+smove :: (KnownSymbol s, KnownSymbol t, Serialize x
+        , (Not (Member xs s) && Not (IsSet (FromJust (Get xs t)))
+            || SetOrNX' xs s && SetOrNX' xs t) ~ 'True)
+      => Proxy s -> Proxy t -> x -> Edis xs (Set xs t (SetOf x)) (Either Reply Bool)
+smove src dest val = Edis $ Redis.smove (encodeKey src) (encodeKey dest) (encode val)
+
+spop :: (KnownSymbol s, Serialize x
+      , 'Just (SetOf x) ~ Get xs s)
+     => Proxy s
+     -> Edis xs xs (Either Reply (Maybe x))
+spop key = Edis $ Redis.spop (encodeKey key) >>= decodeAsMaybe
+
+srandmember :: (KnownSymbol s, Serialize x
+             , 'Just (SetOf x) ~ Get xs s)
+            => Proxy s
+            -> Edis xs xs (Either Reply (Maybe x))
+srandmember key = Edis $ Redis.srandmember (encodeKey key) >>= decodeAsMaybe
+
+srem :: (KnownSymbol s, Serialize x
+      , 'Just (SetOf x) ~ Get xs s)
+     => Proxy s
+     -> x
+     -> Edis xs xs (Either Reply Integer)
+srem key val = Edis $ Redis.srem (encodeKey key) [encode val]
+
+sunion :: (KnownSymbol s, KnownSymbol t, Serialize x
+        , 'Just (SetOf x) ~ Get xs s  -- must exist
+        , SetOrNX xs s)
+        => Proxy s -> Proxy t
+        -> Edis xs xs (Either Reply [x])
+sunion key key' = Edis $ Redis.sunion [encodeKey key, encodeKey key'] >>= decodeAsList
+
+sunionstore :: (KnownSymbol s, KnownSymbol t, SetOrNX xs s, SetOrNX xs t)
+        => Proxy s -> Proxy t
+        -> Edis xs xs (Either Reply Integer)
+sunionstore dest key = Edis $ Redis.sunionstore (encodeKey dest) [encodeKey key]
diff --git a/src/Database/Edis/Command/String.hs b/src/Database/Edis/Command/String.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/String.hs
@@ -0,0 +1,111 @@
+{-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeOperators, OverloadedStrings #-}
+
+module Database.Edis.Command.String where
+
+import Database.Edis.Type
+import Database.Edis.Helper
+
+import Data.ByteString          (ByteString)
+import Data.Proxy               (Proxy)
+import Data.Serialize           (Serialize, encode)
+import Data.Type.Bool
+import Database.Redis as Redis hiding (decode)
+import GHC.TypeLits
+
+
+--------------------------------------------------------------------------------
+--  String
+--------------------------------------------------------------------------------
+
+append :: (KnownSymbol s, Serialize x, StringOrNX xs s)
+        => Proxy s -> x -> Edis xs (Set xs s (StringOf ByteString)) (Either Reply Integer)
+append key val = Edis $ Redis.append (encodeKey key) (encode val)
+
+bitcount :: (KnownSymbol s, StringOrNX xs s)
+        => Proxy s -> Edis xs xs (Either Reply Integer)
+bitcount key = Edis $ Redis.bitcount (encodeKey key)
+
+bitcountRange :: (KnownSymbol s, StringOrNX xs s)
+        => Proxy s -> Integer -> Integer -> Edis xs xs (Either Reply Integer)
+bitcountRange key m n = Edis $ Redis.bitcountRange (encodeKey key) m n
+
+--  key must be String or non-existent
+bitopAnd :: (KnownSymbol s, KnownSymbol t, StringOrNX xs s)
+        => Proxy t -> Proxy s -> Edis xs (Set xs s (StringOf ByteString)) (Either Reply Integer)
+bitopAnd dest key = Edis $ Redis.bitopAnd (encodeKey dest) [encodeKey key]
+
+bitopOr :: (KnownSymbol s, KnownSymbol t, StringOrNX xs s)
+        => Proxy t -> Proxy s -> Edis xs (Set xs s (StringOf ByteString)) (Either Reply Integer)
+bitopOr dest key = Edis $ Redis.bitopOr (encodeKey dest) [encodeKey key]
+
+bitopXor :: (KnownSymbol s, KnownSymbol t, StringOrNX xs s)
+        => Proxy t -> Proxy s -> Edis xs (Set xs s (StringOf ByteString)) (Either Reply Integer)
+bitopXor dest key = Edis $ Redis.bitopXor (encodeKey dest) [encodeKey key]
+
+bitopNot :: (KnownSymbol s, KnownSymbol t, StringOrNX xs s)
+        => Proxy t -> Proxy s -> Edis xs (Set xs s (StringOf ByteString)) (Either Reply Integer)
+bitopNot dest key = Edis $ Redis.bitopNot (encodeKey dest) (encodeKey key)
+
+decr :: (KnownSymbol s, StringOfIntegerOrNX xs s)
+        => Proxy s -> Edis xs (Set xs s (StringOf Integer)) (Either Reply Integer)
+decr key = Edis $ Redis.decr (encodeKey key)
+
+decrby :: (KnownSymbol s, StringOfIntegerOrNX xs s)
+        => Proxy s -> Integer -> Edis xs (Set xs s (StringOf Integer)) (Either Reply Integer)
+decrby key n = Edis $ Redis.decrby (encodeKey key) n
+
+get :: (KnownSymbol s, Serialize x, StringOf x ~ FromJust (Get xs s))
+        => Proxy s -> Edis xs xs (Either Reply (Maybe x))
+get key = Edis $ Redis.get (encodeKey key) >>= decodeAsMaybe
+
+getbit :: (KnownSymbol s, StringOrNX xs s)
+        => Proxy s -> Integer -> Edis xs xs (Either Reply Integer)
+getbit key n = Edis $ Redis.getbit (encodeKey key) n
+
+getrange :: (KnownSymbol s, Serialize x, StringOf x ~ FromJust (Get xs s))
+        => Proxy s -> Integer -> Integer -> Edis xs xs (Either Reply x)
+getrange key n m = Edis $ Redis.getrange (encodeKey key) n m >>= decodeAsAnything
+
+getset :: (KnownSymbol s, Serialize x, Serialize y, StringOf y ~ FromJust (Get xs s))
+        => Proxy s -> x -> Edis xs xs (Either Reply (Maybe y))
+getset key val = Edis $ Redis.getset (encodeKey key) (encode val) >>= decodeAsMaybe
+
+incr :: (KnownSymbol s, StringOfIntegerOrNX xs s)
+        => Proxy s -> Edis xs xs (Either Reply Integer)
+incr key = Edis $ Redis.incr (encodeKey key)
+
+incrby :: (KnownSymbol s, StringOfIntegerOrNX xs s)
+        => Proxy s -> Integer -> Edis xs xs (Either Reply Integer)
+incrby key n = Edis $ Redis.incrby (encodeKey key) n
+
+incrbyfloat :: (KnownSymbol s, StringOfDoubleOrNX xs s)
+        => Proxy s -> Double -> Edis xs xs (Either Reply Double)
+incrbyfloat key n = Edis $ Redis.incrbyfloat (encodeKey key) n
+
+psetex :: (KnownSymbol s, Serialize x)
+        => Proxy s -> Integer -> x -> Edis xs (Set xs s (StringOf x)) (Either Reply Status)
+psetex key n val = Edis $ Redis.psetex (encodeKey key) n (encode val)
+
+set :: (KnownSymbol s, Serialize x)
+        => Proxy s -> x -> Edis xs (Set xs s (StringOf x)) (Either Reply Status)
+set key val = Edis $ Redis.set (encodeKey key) (encode val)
+
+setbit :: (KnownSymbol s, Serialize x, StringOrNX xs s)
+        => Proxy s -> Integer -> x -> Edis xs (Set xs s (StringOf x)) (Either Reply Integer)
+setbit key n val = Edis $ Redis.setbit (encodeKey key) n (encode val)
+
+setex :: (KnownSymbol s, Serialize x)
+        => Proxy s -> Integer -> x -> Edis xs (Set xs s (StringOf x)) (Either Reply Status)
+setex key n val = Edis $ Redis.setex (encodeKey key) n (encode val)
+
+setnx :: (KnownSymbol s, Serialize x)
+        => Proxy s -> x -> Edis xs (If (Member xs s) xs (Set xs s (StringOf x))) (Either Reply Bool)
+setnx key val = Edis $ Redis.setnx (encodeKey key) (encode val)
+
+setrange :: (KnownSymbol s, Serialize x, StringOrNX xs s)
+        => Proxy s -> Integer -> x -> Edis xs (Set xs s (StringOf x)) (Either Reply Integer)
+setrange key n val = Edis $ Redis.setrange (encodeKey key) n (encode val)
+
+strlen :: (KnownSymbol s, StringOrNX xs s)
+        => Proxy s -> Edis xs xs (Either Reply Integer)
+strlen key = Edis $ Redis.strlen (encodeKey key)
diff --git a/src/Database/Edis/Command/ZSet.hs b/src/Database/Edis/Command/ZSet.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Command/ZSet.hs
@@ -0,0 +1,147 @@
+{-# LANGUAGE DataKinds, PolyKinds, TypeFamilies, TypeOperators, OverloadedStrings #-}
+
+module Database.Edis.Command.ZSet where
+
+import Database.Edis.Type
+import Database.Edis.Helper
+
+import Data.Proxy               (Proxy)
+import Data.Serialize           (Serialize, encode)
+import Data.Type.Bool
+import Database.Redis as Redis hiding (decode)
+import GHC.TypeLits
+
+--------------------------------------------------------------------------------
+--  Sorted Sets
+--------------------------------------------------------------------------------
+
+zadd :: (KnownSymbol s, Serialize x, ZSetOrNX xs s)
+        => Proxy s -> Double -> x
+        -> Edis xs (If (Member xs s) xs (Set xs s (ZSetOf x))) (Either Reply Integer)
+zadd key score val = Edis $ Redis.zadd (encodeKey key) [(score, encode val)]
+
+
+zcard :: (KnownSymbol s, SetOrNX xs s)
+        => Proxy s
+        -> Edis xs xs (Either Reply Integer)
+zcard key = Edis $ Redis.zcard (encodeKey key)
+
+zcount :: (KnownSymbol s, SetOrNX xs s)
+        => Proxy s -> Double -> Double
+        -> Edis xs xs (Either Reply Integer)
+zcount key min' max' = Edis $ Redis.zcount (encodeKey key) min' max'
+
+zincrby :: (KnownSymbol s, Serialize x, ZSetOrNX xs s)
+        => Proxy s -> Integer -> x
+        -> Edis xs (If (Member xs s) xs (Set xs s (ZSetOf x))) (Either Reply Double)
+zincrby key score val = Edis $ Redis.zincrby (encodeKey key) score (encode val)
+
+zrange :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Integer -> Integer
+        -> Edis xs xs (Either Reply [x])
+zrange key from to = Edis $ Redis.zrange (encodeKey key) from to >>= decodeAsList
+
+zrangeWithscores :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Integer -> Integer
+        -> Edis xs xs (Either Reply [(x, Double)])
+zrangeWithscores key from to =
+    Edis $ Redis.zrangeWithscores (encodeKey key) from to >>= decodeAsListWithScores
+
+zrangebyscore :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Double -> Double
+        -> Edis xs xs (Either Reply [x])
+zrangebyscore key min' max' =
+    Edis $ Redis.zrangebyscore (encodeKey key) min' max' >>= decodeAsList
+
+zrangebyscoreWithscores :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Double -> Double
+        -> Edis xs xs (Either Reply [(x, Double)])
+zrangebyscoreWithscores key min' max' =
+    Edis $ Redis.zrangebyscoreWithscores (encodeKey key) min' max' >>= decodeAsListWithScores
+
+zrangebyscoreLimit :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Double -> Double -> Integer -> Integer
+        -> Edis xs xs (Either Reply [x])
+zrangebyscoreLimit key min' max' offset count =
+    Edis $ Redis.zrangebyscoreLimit (encodeKey key) min' max' offset count >>= decodeAsList
+
+zrangebyscoreWithscoresLimit :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Double -> Double -> Integer -> Integer
+        -> Edis xs xs (Either Reply [(x, Double)])
+zrangebyscoreWithscoresLimit key min' max' offset count =
+    Edis $ Redis.zrangebyscoreWithscoresLimit (encodeKey key) min' max' offset count >>= decodeAsListWithScores
+
+zrank :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> x
+        -> Edis xs xs (Either Reply (Maybe Integer))
+zrank key val = Edis $ Redis.zrank (encodeKey key) (encode val)
+
+zremrangebyrank :: (KnownSymbol s, ZSetOrNX xs s)
+        => Proxy s -> Integer -> Integer
+        -> Edis xs xs (Either Reply Integer)
+zremrangebyrank key from to = Edis $ Redis.zremrangebyrank (encodeKey key) from to
+
+zremrangebyscore :: (KnownSymbol s, ZSetOrNX xs s)
+        => Proxy s -> Double -> Double
+        -> Edis xs xs (Either Reply Integer)
+zremrangebyscore key min' max' = Edis $ Redis.zremrangebyscore (encodeKey key) min' max'
+
+zrevrange :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Integer -> Integer
+        -> Edis xs xs (Either Reply [x])
+zrevrange key from to = Edis $ Redis.zrevrange (encodeKey key) from to >>= decodeAsList
+
+zrevrangeWithscores :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Integer -> Integer
+        -> Edis xs xs (Either Reply [(x, Double)])
+zrevrangeWithscores key from to =
+    Edis $ Redis.zrevrangeWithscores (encodeKey key) from to >>= decodeAsListWithScores
+
+zrevrangebyscore :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Double -> Double
+        -> Edis xs xs (Either Reply [x])
+zrevrangebyscore key min' max' =
+    Edis $ Redis.zrevrangebyscore (encodeKey key) min' max' >>= decodeAsList
+
+zrevrangebyscoreWithscores :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Double -> Double
+        -> Edis xs xs (Either Reply [(x, Double)])
+zrevrangebyscoreWithscores key min' max' =
+    Edis $ Redis.zrevrangebyscoreWithscores (encodeKey key) min' max' >>= decodeAsListWithScores
+
+zrevrangebyscoreLimit :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Double -> Double -> Integer -> Integer
+        -> Edis xs xs (Either Reply [x])
+zrevrangebyscoreLimit key min' max' offset count =
+    Edis $ Redis.zrevrangebyscoreLimit (encodeKey key) min' max' offset count >>= decodeAsList
+
+zrevrangebyscoreWithscoresLimit :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> Double -> Double -> Integer -> Integer
+        -> Edis xs xs (Either Reply [(x, Double)])
+zrevrangebyscoreWithscoresLimit key min' max' offset count =
+    Edis $ Redis.zrevrangebyscoreWithscoresLimit (encodeKey key) min' max' offset count >>= decodeAsListWithScores
+
+zrevrank :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> x
+        -> Edis xs xs (Either Reply (Maybe Integer))
+zrevrank key val = Edis $ Redis.zrevrank (encodeKey key) (encode val)
+
+zscore :: (KnownSymbol s, Serialize x
+        , 'Just (ZSetOf x) ~ Get xs s)
+        => Proxy s -> x
+        -> Edis xs xs (Either Reply (Maybe Double))
+zscore key val = Edis $ Redis.zscore (encodeKey key) (encode val)
diff --git a/src/Database/Edis/Helper.hs b/src/Database/Edis/Helper.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Helper.hs
@@ -0,0 +1,57 @@
+{-# OPTIONS_HADDOCK hide #-}
+
+module Database.Edis.Helper where
+
+import Data.ByteString          (ByteString)
+import Data.Maybe               (fromJust)
+import Data.Proxy               (Proxy)
+import Data.Serialize           (Serialize, encode, decode)
+import Database.Redis as Redis hiding (decode)
+import GHC.TypeLits
+
+--------------------------------------------------------------------------------
+--  Helper functions
+--------------------------------------------------------------------------------
+
+encodeKey :: KnownSymbol s => Proxy s -> ByteString
+encodeKey = encode . symbolVal
+
+decodeAsAnything :: (Serialize x) => Either Reply ByteString -> Redis (Either Reply x)
+decodeAsAnything (Left replyErr) = return $ Left replyErr
+decodeAsAnything (Right str) = case decode str of
+        Left decodeErr -> return $ Left (Error $ encode decodeErr)
+        Right val      -> return $ Right val
+
+decodeAsMaybe :: (Serialize x) => Either Reply (Maybe ByteString) -> Redis (Either Reply (Maybe x))
+decodeAsMaybe (Left replyErr) = return $ Left replyErr
+decodeAsMaybe (Right Nothing) = return $ Right Nothing
+decodeAsMaybe (Right (Just str)) = case decode str of
+        Left decodeErr -> return $ Left (Error $ encode decodeErr)
+        Right val      -> return $ Right (Just val)
+
+decodeBPOP :: Serialize x => Either Reply (Maybe (ByteString, ByteString)) -> Redis (Either Reply (Maybe (ByteString, x)))
+decodeBPOP (Left replyError)         = return $ Left replyError
+decodeBPOP (Right Nothing)           = return $ Right Nothing
+decodeBPOP (Right (Just (key, raw))) = case decode raw of
+    Left decodeError -> return $ Left (Error $ encode decodeError)
+    Right value      -> return $ Right (Just (key, value))
+
+decodeAsList :: (Serialize x) => Either Reply [ByteString] -> Redis (Either Reply [x])
+decodeAsList (Left replyErr) = return $ Left replyErr
+decodeAsList (Right strs) = case mapM decode strs of
+    Left decodeErr -> return $ Left (Error $ encode decodeErr)
+    Right vals     -> return $ Right vals
+
+decodeAsListWithScores :: (Serialize x) => Either Reply [(ByteString, Double)] -> Redis (Either Reply [(x, Double)])
+decodeAsListWithScores (Left replyErr) = return $ Left replyErr
+decodeAsListWithScores (Right pairs) = let (strs, scores) = unzip pairs in
+    case mapM decode strs of
+        Left decodeErr -> return $ Left (Error $ encode decodeErr)
+        Right vals     -> return $ Right (zip vals scores)
+
+fromRight :: Either a b -> b
+fromRight (Left _) = error "Left val"
+fromRight (Right e) = e
+
+fromMaybeResult :: Either Reply (Maybe x) -> x
+fromMaybeResult = fromJust . fromRight
diff --git a/src/Database/Edis/Type.hs b/src/Database/Edis/Type.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/Edis/Type.hs
@@ -0,0 +1,191 @@
+{-# LANGUAGE DataKinds, ConstraintKinds, PolyKinds,
+    TypeFamilies, TypeOperators #-}
+
+module Database.Edis.Type where
+
+import           GHC.TypeLits
+
+import           Data.Type.Bool
+import           Data.Type.Equality
+import           Database.Redis (Redis)
+
+--------------------------------------------------------------------------------
+--  Maybe
+--------------------------------------------------------------------------------
+
+--  FromJust : (Maybe k) -> k
+type family FromJust (x :: Maybe k) :: k where
+    FromJust ('Just k) = k
+
+--------------------------------------------------------------------------------
+--  Dictionary Membership
+--------------------------------------------------------------------------------
+
+-- Member :: Key -> [ (Key, Type) ] -> Bool
+type family Member (xs :: [ (Symbol, *) ]) (s :: Symbol) :: Bool where
+    Member '[]             s = 'False
+    Member ('(s, x) ': xs) s = 'True
+    Member ('(t, x) ': xs) s = Member xs s
+
+-- memberEx0 :: (Member "C" '[] ~ False) => ()
+-- memberEx0 = ()
+--
+-- memberEx1 :: (Member "A" '[ '("A", Char), '("B", Int) ] ~ True) => ()
+-- memberEx1 = ()
+--
+-- memberEx2 :: (Member "C" '[ '("A", Char), '("B", Int) ] ~ False) => ()
+-- memberEx2 = ()
+
+--------------------------------------------------------------------------------
+--  Dictionary Lookup
+--------------------------------------------------------------------------------
+
+-- type family Get' (s :: Symbol) (xs :: [ (Symbol, *) ]) :: * where
+--     Get' s ('(s, x) ': xs) = x
+--     Get' s ('(t, x) ': xs) = Get' s xs
+--
+-- getEx0' :: (Get' "A" '[ '("A", Char), '("B", Int) ] ~ Char) => ()
+-- getEx0' = ()
+--
+-- getEx1' :: (Get' "C" '[ '("A", Char), '("B", Int) ] ~ Char) => ()
+-- getEx1' = ()
+
+-- Get :: Key -> [ (Key, Type) ] -> Maybe Type
+type family Get
+    (xs :: [ (Symbol, *) ])
+    (s :: Symbol)
+    :: Maybe * where
+
+    Get '[]             s = 'Nothing
+    Get ('(s, x) ': xs) s = 'Just x
+    Get ('(t, x) ': xs) s = Get xs s
+
+
+-- getEx0 :: (Get "A" '[ '("A", Char), '("B", Int) ] ~ Just Char) => ()
+-- getEx0 = ()
+--
+-- getEx1 :: (Get "C" '[ '("A", Char), '("B", Int) ] ~ Nothing) => ()
+-- getEx1 = ()
+
+
+--------------------------------------------------------------------------------
+--  Dictionary Set
+--------------------------------------------------------------------------------
+
+-- Set :: Key -> Type -> [ (Key, Type) ] -> [ (Key, Type) ]
+type family Set (xs :: [ (Symbol, *) ]) (s :: Symbol) (x :: *) :: [ (Symbol, *) ] where
+    Set '[]             s x = '[ '(s, x) ]
+    Set ('(s, y) ': xs) s x = ('(s, x) ': xs)
+    Set ('(t, y) ': xs) s x = '(t, y) ': (Set xs s x)
+
+-- setEx0 :: (Set "A" Char '[] ~ '[ '("A", Char) ]) => ()
+-- setEx0 = ()
+--
+-- setEx1 :: (Set "A" Bool '[ '("A", Char) ] ~ '[ '("A", Bool) ]) => ()
+-- setEx1 = ()
+
+--------------------------------------------------------------------------------
+--  Dictionary Deletion
+--------------------------------------------------------------------------------
+
+-- Del :: Key -> [ (Key, Type) ] -> [ (Key, Type) ]
+type family Del (xs :: [ (Symbol, *) ]) (s :: Symbol) :: [ (Symbol, *) ] where
+    Del '[] s             = '[]
+    Del ('(s, y) ': xs) s = xs
+    Del ('(t, y) ': xs) s = '(t, y) ': (Del xs s)
+
+-- delEx0 :: (Del "A" '[ '("A", Char) ] ~ '[]) => ()
+-- delEx0 = ()
+--
+-- delEx1 :: (Del "A" '[ '("B", Int), '("A", Char) ] ~ '[ '("B", Int) ]) => ()
+-- delEx1 = ()
+
+--------------------------------------------------------------------------------
+--  P
+--------------------------------------------------------------------------------
+
+class IMonad m where
+    unit :: a -> m p p a
+    bind :: m p q a -> (a -> m q r b) -> m p r b
+
+newtype Edis p q a = Edis { unEdis :: Redis a }
+
+instance IMonad Edis where
+    unit = Edis . return
+    bind m f = Edis (unEdis m >>= unEdis . f )
+
+infixl 1 >>>
+
+(>>>) :: IMonad m => m p q a -> m q r b -> m p r b
+a >>> b = bind a (const b)
+
+--------------------------------------------------------------------------------
+--  Redis Data Types
+--------------------------------------------------------------------------------
+
+data U n = String' n | Hash' n
+
+data StringOf :: * -> *
+data HashOf :: [ (Symbol, *) ] -> *
+data ListOf :: * -> *
+data SetOf :: * -> *
+data ZSetOf :: * -> *
+
+--------------------------------------------------------------------------------
+--  Redis Data Type
+--------------------------------------------------------------------------------
+
+type family IsString (x :: *) :: Bool where
+    IsString (StringOf n) = 'True
+    IsString x            = 'False
+
+type StringOrNX xs s          = (IsString (FromJust (Get xs s)) || Not (Member xs s)) ~ 'True
+type StringOfIntegerOrNX xs s = ((FromJust (Get xs s) == Integer) || Not (Member xs s)) ~ 'True
+type StringOfDoubleOrNX xs s  = ((FromJust (Get xs s) == Double) || Not (Member xs s)) ~ 'True
+
+type family IsHash (x :: *) :: Bool where
+    IsHash (HashOf n) = 'True
+    IsHash x          = 'False
+
+type HashOrNX xs k = (IsHash (FromJust (Get xs k)) ||  Not (Member xs k)) ~ 'True
+
+type family IsList (x :: *) :: Bool where
+    IsList (ListOf n) = 'True
+    IsList x          = 'False
+
+type ListOrNX xs s = (IsList (FromJust (Get xs s)) || Not (Member xs s)) ~ 'True
+
+type family IsSet (x :: *) :: Bool where
+    IsSet (SetOf n) = 'True
+    IsSet x         = 'False
+
+type SetOrNX xs s = (IsSet (FromJust (Get xs s)) || Not (Member xs s)) ~ 'True
+type SetOrNX' xs s = (IsSet (FromJust (Get xs s)) || Not (Member xs s))
+
+type family IsZSet (x :: *) :: Bool where
+    IsZSet (ZSetOf n) = 'True
+    IsZSet x          = 'False
+
+type ZSetOrNX xs s = (IsZSet (FromJust (Get xs s)) || Not (Member xs s)) ~ 'True
+
+type family GetHash (xs :: [ (Symbol, *) ]) (k :: Symbol) (f :: Symbol) :: Maybe * where
+    GetHash '[]                    k f = 'Nothing
+    GetHash ('(k, HashOf hs) ': xs) k f = Get hs f
+    GetHash ('(k, x       ) ': xs) k f = 'Nothing
+    GetHash ('(l, y       ) ': xs) k f = GetHash xs k f
+
+type family SetHash (xs :: [ (Symbol, *) ]) (k :: Symbol) (f :: Symbol) (x :: *) :: [ (Symbol, *) ] where
+    SetHash '[]                    k f x = '(k, HashOf (Set '[] f x)) ': '[]
+    SetHash ('(k, HashOf hs) ': xs) k f x = '(k, HashOf (Set hs  f x)) ': xs
+    SetHash ('(l, y       ) ': xs) k f x = '(l, y                  ) ': SetHash xs k f x
+
+type family DelHash (xs :: [ (Symbol, *) ]) (k :: Symbol) (f :: Symbol) :: [ (Symbol, *) ] where
+    DelHash '[]                    k f = '[]
+    DelHash ('(k, HashOf hs) ': xs) k f = '(k, HashOf (Del hs f )) ': xs
+    DelHash ('(l, y       ) ': xs) k f = '(l, y                ) ': DelHash xs k f
+
+type family MemHash (xs :: [ (Symbol, *) ]) (k :: Symbol) (f :: Symbol) :: Bool where
+    MemHash '[]                    k f = 'False
+    MemHash ('(k, HashOf hs) ': xs) k f = Member hs f
+    MemHash ('(k, x       ) ': xs) k f = 'False
+    MemHash ('(l, y       ) ': xs) k f = MemHash xs k f
