{-
Copyright (c) 2010-2011, Alexander Bogdanov <andorn@gmail.com>
License: MIT
-}

{-# LANGUAGE FlexibleContexts, OverloadedStrings, PackageImports #-}

-- | Monadic wrapper for "Database.Redis.Redis"
module Database.Redis.Monad (
       -- * Types ans Constructors
       WithRedis(..),
       R.Redis(..),
       R.Reply(..),
       R.Message(..),
       R.LInsertDirection(..),
       R.Interval(..),
       R.IsInterval(..),
       R.SortOptions(..),
       R.Aggregate(..),
       R.RedisKeyType,
       R.RedisInfo,
       R.sortDefaults,

       R.fromRInline, R.fromRBulk, R.fromRMulti, R.fromRMultiBulk,
       R.fromRMultiBulk', R.fromRInt, R.fromROk, R.noError, R.parseMessage,
       R.takeAll,

       -- * Database connection
        R.localhost, R.defaultPort,
       connect, disconnect, isConnected,
       getServer, getDatabase, renameCommand,

       -- * Redis commands
       -- ** Generic
       ping, auth, echo, quit, shutdown,
       multi, exec, discard, run_multi,
       watch, unwatch, run_cas, exists,
       del, del_, getType, keys, randomKey, rename,
       renameNx, dbsize, expire, expireAt,
       persist, ttl, select, move, flushDb,
       flushAll, info,

       -- ** Strings
       set, setNx,setEx, mSet, mSetNx,
       get, getSet, mGet,
       incr, incrBy, incrByFloat, decr,
       decrBy, append, substr,
       getrange, setrange,
       getbit, setbit,strlen,

       -- ** Lists
       rpush, rpush_, lpush, rpushx, lpushx,
       llen, lrange, ltrim,
       lindex, lset, lrem, lpop, rpop,
       rpoplpush, blpop, brpop, brpoplpush,

       -- ** Sets
       sadd, sadd_, srem, srem_, spop, smove, scard, sismember,
       smembers, srandmember, sinter, sinterStore,
       sunion, sunionStore, sdiff, sdiffStore,

       -- ** Sorted sets
       zadd, zadd_, zrem, zrem_, zincrBy, zrange,
       zrevrange, zrangebyscore, zrevrangebyscore,
       zcount, zremrangebyscore, zcard, zscore,
       zrank, zrevrank, zremrangebyrank,
       zunion, zinter, zunionStore, zinterStore,

       -- ** Hashes
       hset, hget, hdel, hdel_, hmset, hmget,
       hincrBy, hincrByFloat, hexists, hlen,
       hkeys, hvals, hgetall,

       -- ** Sorting
       sort, listRelated,

       -- ** Publish/Subscribe
       subscribed, subscribe, unsubscribe,
       psubscribe, punsubscribe, publish,
       listen,

       -- ** Persistent control
       save, bgsave, lastsave, bgrewriteaof
)
where

import "mtl" Control.Monad.Trans
import "MonadCatchIO-mtl" Control.Monad.CatchIO
import Data.ByteString (ByteString)

import qualified Database.Redis.Redis as R
import Database.Redis.Redis (IsInterval)
import Database.Redis.ByteStringClass
import qualified Database.Redis.Internal as Internal

class MonadIO m => WithRedis m where
    getRedis :: m (R.Redis)
    setRedis :: R.Redis -> m ()

connect :: WithRedis m => String -> String -> m ()
connect host port = liftIO (R.connect host port) >>= setRedis

disconnect :: WithRedis m => m ()
disconnect = getRedis >>= liftIO . R.disconnect

isConnected :: WithRedis m => m Bool
isConnected = getRedis >>= liftIO . R.isConnected

getServer :: WithRedis m => m (String, String)
getServer = getRedis >>= liftIO . R.getServer

getDatabase :: WithRedis m => m Int
getDatabase = getRedis >>= liftIO . R.getDatabase

renameCommand :: WithRedis m => ByteString -> ByteString -> m ()
renameCommand c c' = do r <- getRedis
                        liftIO $ R.renameCommand r c c'

ping :: WithRedis m => m (R.Reply ())
ping = getRedis >>= liftIO . R.ping

auth :: WithRedis m => String -> m (R.Reply ())
auth pwd = getRedis >>= liftIO . flip R.auth pwd

echo :: (WithRedis m, BS s) => s -> m (R.Reply s)
echo s = getRedis >>= liftIO . flip R.echo s

quit :: WithRedis m => m ()
quit = getRedis >>= liftIO . R.quit

shutdown :: WithRedis m => m ()
shutdown = getRedis >>= liftIO . R.shutdown

multi :: WithRedis m => m (R.Reply ())
multi = getRedis >>= liftIO . R.multi

exec :: (WithRedis m, BS s) => m (R.Reply s)
exec = getRedis >>= liftIO . R.exec

discard :: WithRedis m => m (R.Reply ())
discard = getRedis >>= liftIO . R.discard

run_multi :: (MonadCatchIO m, WithRedis m, BS s) => m () -> m (R.Reply s)
run_multi cs = do r <- getRedis
                  bracket (liftIO $ Internal.takeState r)
                          (\_ -> liftIO $ Internal.putStateUnmodified r)
                          (\rs -> do liftIO $ (Internal.sendCommand rs (Internal.CInline "MULTI") >> (Internal.recv rs :: IO (Internal.Reply ())))
                                     cs `onException` (liftIO $ do Internal.sendCommand rs (Internal.CInline "DISCARD")
                                                                   Internal.recv rs :: IO (Internal.Reply ()))
                                     liftIO $ Internal.sendCommand rs (Internal.CInline "EXEC")
                                     liftIO $ Internal.recv rs)

watch :: (WithRedis m, BS s) => [s] -> m (R.Reply ())
watch cs = getRedis >>= liftIO . flip R.watch cs

unwatch :: WithRedis m => m (R.Reply ())
unwatch = getRedis >>= liftIO . R.unwatch

run_cas :: (MonadCatchIO m, WithRedis m, BS s1) => [s1] -> m a -> m a
run_cas keys cs = let keys' = map toBS keys
                  in do r <- getRedis
                        bracket (liftIO $ Internal.takeState r)
                                (\_ -> liftIO $ Internal.putStateUnmodified r)
                                (\rs -> do liftIO $ (Internal.sendCommand rs (Internal.CMBulk ("WATCH" : keys')))
                                                      >> (Internal.recv rs :: IO (Internal.Reply ()))
                                                      >>= R.fromROk
                                           res <- cs `onException` (liftIO $ do Internal.sendCommand rs (Internal.CInline "DISCARD")
                                                                                Internal.recv rs :: IO (Internal.Reply ())
                                                                                Internal.sendCommand rs (Internal.CInline "UNWATCH")
                                                                                Internal.recv rs :: IO (Internal.Reply ()))
                                           liftIO $ (Internal.sendCommand rs (Internal.CInline "UNWATCH"))
                                                      >> (Internal.recv rs :: IO (Internal.Reply ()))
                                                      >>= R.fromROk
                                           return res)

exists :: (WithRedis m, BS s) => s -> m (R.Reply Int)
exists key = getRedis >>= liftIO . flip R.exists key

del :: (WithRedis m, BS s) => s -> m (R.Reply Int)
del key = getRedis >>= liftIO . flip R.del key

del_ :: (WithRedis m, BS s) => [s] -> m (R.Reply Int)
del_ keys = getRedis >>= liftIO . flip R.del_ keys

getType :: (WithRedis m, BS s1) => s1 -> m R.RedisKeyType
getType key = getRedis >>= liftIO . flip R.getType key

keys :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
keys pattern = getRedis >>= liftIO . flip R.keys pattern

randomKey :: (WithRedis m, BS s) => m (R.Reply s)
randomKey = getRedis >>= liftIO . R.randomKey

rename :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply ())
rename from to = do r <- getRedis
                    liftIO $ R.rename r from to

renameNx :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
renameNx from to = do r <- getRedis
                      liftIO $ R.renameNx r from to

dbsize :: WithRedis m => m (R.Reply Int)
dbsize = getRedis >>= liftIO . R.dbsize

expire :: (WithRedis m, BS s) => s -> Int -> m (R.Reply Int)
expire key seconds = do r <- getRedis
                        liftIO $ R.expire r key seconds

expireAt :: (WithRedis m, BS s) => s -> Int -> m (R.Reply Int)
expireAt key timestamp = do r <- getRedis
                            liftIO $ R.expireAt r key timestamp

persist :: (WithRedis m, BS s) => s -> m (R.Reply Int)
persist key = getRedis >>= liftIO . flip R.persist key

ttl :: (WithRedis m, BS s) => s -> m (R.Reply Int)
ttl key = getRedis >>= liftIO . flip R.ttl key

select :: WithRedis m => Int -> m (R.Reply ())
select db = getRedis >>= liftIO . flip R.select db

move :: (WithRedis m, BS s) => s -> Int -> m (R.Reply Int)
move key db = do r <- getRedis
                 liftIO $ R.move r key db

flushDb :: WithRedis m => m (R.Reply ())
flushDb = getRedis >>= liftIO . R.flushDb

flushAll :: WithRedis m => m (R.Reply ())
flushAll = getRedis >>= liftIO . R.flushAll

info :: WithRedis m => m R.RedisInfo
info = getRedis >>= liftIO . R.info

set :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply ())
set key val = do r <- getRedis
                 liftIO $ R.set r key val

setNx :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
setNx key val = do r <- getRedis
                   liftIO $ R.setNx r key val

setEx :: (WithRedis m, BS s1, BS s2) => s1 -> Int -> s2 -> m (R.Reply ())
setEx key seconds val = do r <- getRedis
                           liftIO $ R.setEx r key seconds val

mSet :: (WithRedis m, BS s1, BS s2) => [(s1, s2)] -> m (R.Reply ())
mSet ks = getRedis >>= liftIO . flip R.mSet ks

mSetNx :: (WithRedis m, BS s1, BS s2) => [(s1, s2)] -> m (R.Reply Int)
mSetNx ks = getRedis >>= liftIO . flip R.mSetNx ks

get :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
get key = getRedis >>= liftIO . flip R.get key

getSet :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> s2 -> m (R.Reply s3)
getSet key val = do r <- getRedis
                    liftIO $ R.getSet r key val

mGet :: (WithRedis m, BS s1, BS s2) => [s1] -> m (R.Reply s2)
mGet keys = getRedis >>= liftIO . flip R.mGet keys

incr :: (WithRedis m, BS s) => s -> m (R.Reply Int)
incr key = getRedis >>= liftIO . flip R.incr key

incrBy :: (WithRedis m, BS s) => s -> Int -> m (R.Reply Int)
incrBy key n = do r <- getRedis
                  liftIO $ R.incrBy r key n

incrByFloat :: (WithRedis m, BS s) => s -> Double -> m (R.Reply Double)
incrByFloat key n = do r <- getRedis
                       liftIO $ R.incrByFloat r key n

decr :: (WithRedis m, BS s) => s -> m (R.Reply Int)
decr key = getRedis >>= liftIO . flip R.decr key

decrBy :: (WithRedis m, BS s) => s -> Int -> m (R.Reply Int)
decrBy key n = do r <- getRedis
                  liftIO $ R.decrBy r key n

append :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
append key str = do r <- getRedis
                    liftIO $ R.append r key str

substr :: (WithRedis m, BS s1, BS s2) => s1 -> (Int, Int) -> m (R.Reply s2)
substr key range = do r <- getRedis
                      liftIO $ R.substr r key range

getrange :: (WithRedis m, BS s1, BS s2) => s1 -> (Int, Int) -> m (R.Reply s2)
getrange key range = do r <- getRedis
                        liftIO $ R.getrange r key range

setrange :: (WithRedis m, BS s1, BS s2) => s1 -> Int -> s2 -> m (R.Reply Int)
setrange key offset val = do r <- getRedis
                             liftIO $ R.setrange r key offset val

getbit :: (WithRedis m, BS s) => s -> Int -> m (R.Reply Int)
getbit key offset = do r <- getRedis
                       liftIO $ R.getbit r key offset

setbit :: (WithRedis m, BS s) => s -> Int -> Int -> m (R.Reply Int)
setbit key offset bit = do r <- getRedis
                           liftIO $ R.setbit r key offset bit

strlen :: (WithRedis m, BS s) => s -> m (R.Reply Int)
strlen key = getRedis >>= liftIO . flip R.strlen key

rpush :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
rpush key val = do r <- getRedis
                   liftIO $ R.rpush r key val

rpush_ :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> m (R.Reply Int)
rpush_ key vals = do r <- getRedis
                     liftIO $ R.rpush_ r key vals

lpush :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
lpush key val = do r <- getRedis
                   liftIO $ R.lpush r key val

lpush_ :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> m (R.Reply Int)
lpush_ key vals = do r <- getRedis
                     liftIO $ R.lpush_ r key vals

rpushx :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
rpushx key val = do r <- getRedis
                    liftIO $ R.rpushx r key val

lpushx :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
lpushx key val = do r <- getRedis
                    liftIO $ R.lpushx r key val

linsert :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> R.LInsertDirection -> s2 -> s3 -> m (R.Reply Int)
linsert key direction anchor value = do r <- getRedis
                                        liftIO $ R.linsert r key direction anchor value

llen :: (WithRedis m, BS s) => s -> m (R.Reply Int)
llen key = getRedis >>= liftIO . flip R.llen key

lrange :: (WithRedis m, BS s1, BS s2) => s1 -> (Int, Int) -> m (R.Reply s2)
lrange key limit = do r <- getRedis
                      liftIO $ R.lrange r key limit

ltrim :: (WithRedis m, BS s) => s -> (Int, Int) -> m (R.Reply ())
ltrim key limit = do r <- getRedis
                     liftIO $ R.ltrim r key limit

lindex :: (WithRedis m, BS s1, BS s2) => s1 -> Int -> m (R.Reply s2)
lindex key index = do r <- getRedis
                      liftIO $ R.lindex r key index

lset :: (WithRedis m, BS s1, BS s2) => s1 -> Int -> s2 -> m (R.Reply ())
lset key index val = do r <- getRedis
                        liftIO $ R.lset r key index val

lrem :: (WithRedis m, BS s1, BS s2) => s1 -> Int -> s2 -> m (R.Reply Int)
lrem key count value = do r <- getRedis
                          liftIO $ R.lrem r key count value

lpop :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
lpop key = getRedis >>= liftIO . flip R.lpop key

rpop :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
rpop key = getRedis >>= liftIO . flip R.rpop key

rpoplpush :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> s2 -> m (R.Reply s3)
rpoplpush src dst = do r <- getRedis
                       liftIO $ R.rpoplpush r src dst

blpop :: (WithRedis m, BS s1, BS s2) => [s1] -> Int -> m (Maybe (s1, s2))
blpop keys timeout = do r <- getRedis
                        liftIO $ R.blpop r keys timeout

brpop :: (WithRedis m, BS s1, BS s2) => [s1] -> Int -> m (Maybe (s1, s2))
brpop keys timeout = do r <- getRedis
                        liftIO $ R.brpop r keys timeout

brpoplpush :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> s2 -> Int -> m (Maybe (Maybe s3))
brpoplpush src dst timeout = do r <- getRedis
                                liftIO $ R.brpoplpush r src dst timeout

sadd :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
sadd key val = do r <- getRedis
                  liftIO $ R.sadd r key val

sadd_ :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> m (R.Reply Int)
sadd_ key vals = do r <- getRedis
                    liftIO $ R.sadd_ r key vals

srem :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
srem key val = do r <- getRedis
                  liftIO $ R.srem r key val

srem_ :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> m (R.Reply Int)
srem_ key vals = do r <- getRedis
                    liftIO $ R.srem_ r key vals

spop :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
spop key = getRedis >>= liftIO . flip R.spop key

smove :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> s2 -> s3 -> m (R.Reply Int)
smove src dst member = do r <- getRedis
                          liftIO $ R.smove r src dst member

scard :: (WithRedis m, BS s) => s -> m (R.Reply Int)
scard key = getRedis >>= liftIO . flip R.scard key

sismember :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
sismember key val = do r <- getRedis
                       liftIO $ R.sismember r key val

smembers :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
smembers key = getRedis >>= liftIO . flip R.smembers key

srandmember :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
srandmember key = getRedis >>= liftIO . flip R.srandmember key

sinter :: (WithRedis m, BS s1, BS s2) => [s1] -> m (R.Reply s2)
sinter key = getRedis >>= liftIO . flip R.sinter key

sinterStore :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> m (R.Reply ())
sinterStore dst keys = do r <- getRedis
                          liftIO $ R.sinterStore r dst keys

sunion :: (WithRedis m, BS s1, BS s2) => [s1] -> m (R.Reply s2)
sunion keys = getRedis >>= liftIO . flip R.sunion keys

sunionStore :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> m (R.Reply ())
sunionStore dst keys = do r <- getRedis
                          liftIO $ R.sunionStore r dst keys

sdiff :: (WithRedis m, BS s1, BS s2) => [s1] -> m (R.Reply s2)
sdiff keys = getRedis >>= liftIO . flip R.sdiff keys

sdiffStore :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> m (R.Reply ())
sdiffStore dst keys = do r <- getRedis
                         liftIO $ R.sdiffStore r dst keys

zadd :: (WithRedis m, BS s1, BS s2) => s1 -> Double -> s2 -> m (R.Reply Int)
zadd key score member = do r <- getRedis
                           liftIO $ R.zadd r key score member

zadd_ :: (WithRedis m, BS s1, BS s2) => s1 -> [(Double, s2)] -> m (R.Reply Int)
zadd_ key elements = do r <- getRedis
                        liftIO $ R.zadd_ r key elements

zrem :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
zrem key member = do r <- getRedis
                     liftIO $ R.zrem r key member

zrem_ :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> m (R.Reply Int)
zrem_ key members = do r <- getRedis
                       liftIO $ R.zrem_ r key members

zincrBy :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> Double -> s2 -> m (R.Reply s3)
zincrBy key score member = do r <- getRedis
                              liftIO $ R.zincrBy r key score member

zrange :: (WithRedis m, BS s1, BS s2) => s1 -> (Int, Int) -> Bool -> m (R.Reply s2)
zrange key limit withscores = do r <- getRedis
                                 liftIO $ R.zrange r key limit withscores

zrevrange :: (WithRedis m, BS s1, BS s2) => s1 -> (Int, Int) -> Bool -> m (R.Reply s2)
zrevrange key limit withscores = do r <- getRedis
                                    liftIO $ R.zrevrange r key limit withscores

zrangebyscore :: (WithRedis m, IsInterval i Double, BS s1, BS s2) => s1 -> i -> Maybe (Int, Int) -> Bool -> m (R.Reply s2)
zrangebyscore key i limit withscores = do r <- getRedis
                                          liftIO $ R.zrangebyscore r key i limit withscores

zrevrangebyscore :: (WithRedis m, IsInterval i Double, BS s1, BS s2) => s1 -> i -> Maybe (Int, Int) -> Bool -> m (R.Reply s2)
zrevrangebyscore key i limit withscores = do r <- getRedis
                                             liftIO $ R.zrevrangebyscore r key i limit withscores

zcount :: (WithRedis m, IsInterval i Double, BS s) => s -> i -> m (R.Reply Int)
zcount key limit = do r <- getRedis
                      liftIO $ R.zcount r key limit

zremrangebyscore :: (WithRedis m, BS s) => s -> (Double, Double) -> m (R.Reply Int)
zremrangebyscore key limit = do r <- getRedis
                                liftIO $ R.zremrangebyscore r key limit

zcard :: (WithRedis m, BS s) => s -> m (R.Reply Int)
zcard key = getRedis >>= liftIO . flip R.zcard key

zscore :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> s2 -> m (R.Reply s3)
zscore key member = do r <- getRedis
                       liftIO $ R.zscore r key member

zrank :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
zrank key member = do r <- getRedis
                      liftIO $ R.zrank r key member

zrevrank :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
zrevrank key member = do r <- getRedis
                         liftIO $ R.zrevrank r key member

zremrangebyrank :: (WithRedis m, BS s) => s -> (Int, Int) -> m (R.Reply Int)
zremrangebyrank key limit = do r <- getRedis
                               liftIO $ R.zremrangebyrank r key limit

zunionStore :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> [Double] -> R.Aggregate -> m (R.Reply Int)
zunionStore dst src weights aggregate = do r <- getRedis
                                           liftIO $ R.zunionStore r dst src weights aggregate

{-# DEPRECATED zunion "ZUNION command was renamed to ZUNIONSTORE" #-}
zunion :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> [Double] -> R.Aggregate -> m (R.Reply Int)
zunion = zunionStore

zinterStore :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> [Double] -> R.Aggregate -> m (R.Reply Int)
zinterStore dst src weights aggregate = do r <- getRedis
                                           liftIO $ R.zinterStore r dst src weights aggregate

{-# DEPRECATED zinter "ZINTER command was renamed to ZINTERSTORE" #-}
zinter :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> [Double] -> R.Aggregate -> m (R.Reply Int)
zinter = zinterStore

hset :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> s2 -> s3 -> m (R.Reply Int)
hset key field value = do r <- getRedis
                          liftIO $ R.hset r key field value

hget :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> s2 -> m (R.Reply s3)
hget key field = do r <- getRedis
                    liftIO $ R.hget r key field

hdel :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
hdel key field = do r <- getRedis
                    liftIO $ R.hdel r key field

hdel_ :: (WithRedis m, BS s1, BS s2) => s1 -> [s2] -> m (R.Reply Int)
hdel_ key fields = do r <- getRedis
                      liftIO $ R.hdel_ r key fields

hmset :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> [(s2, s3)] -> m (R.Reply ())
hmset key fields = do r <- getRedis
                      liftIO $ R.hmset r key fields

hmget :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> [s2] -> m (R.Reply s3)
hmget key fields = do r <- getRedis
                      liftIO $ R.hmget r key fields

hincrBy :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> Int -> m (R.Reply Int)
hincrBy key field n = do r <- getRedis
                         liftIO $ R.hincrBy r key field n

hincrByFloat :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> Double -> m (R.Reply Double)
hincrByFloat key field n = do r <- getRedis
                              liftIO $ R.hincrByFloat r key field n

hexists :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
hexists key field = do r <- getRedis
                       liftIO $ R.hexists r key field

hlen :: (WithRedis m, BS s) => s -> m (R.Reply Int)
hlen key = do r <- getRedis
              liftIO $ R.hlen r key

hkeys :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
hkeys key = do r <- getRedis
               liftIO $ R.hkeys r key

hvals :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
hvals key = do r <- getRedis
               liftIO $ R.hvals r key

hgetall :: (WithRedis m, BS s1, BS s2) => s1 -> m (R.Reply s2)
hgetall key = do r <- getRedis
                 liftIO $ R.hgetall r key

sort :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> R.SortOptions s2 -> m (R.Reply s3)
sort key opt = do r <- getRedis
                  liftIO $ R.sort r key opt

listRelated :: (WithRedis m, BS s1, BS s2, BS s3) => s1 -> s2 -> (Int, Int) -> m (R.Reply s3)
listRelated related key l = do r <- getRedis
                               liftIO $ R.listRelated r related key l

subscribed :: (WithRedis m) => m Int
subscribed = getRedis >>= liftIO . R.subscribed

subscribe :: (WithRedis m, BS s1, BS s2) => [s1] -> m [R.Message s2]
subscribe classes = getRedis >>= liftIO . flip R.subscribe classes

unsubscribe :: (WithRedis m, BS s1, BS s2) => [s1] -> m [R.Message s2]
unsubscribe classes = getRedis >>= liftIO . flip R.unsubscribe classes

psubscribe :: (WithRedis m, BS s1, BS s2) => [s1] -> m [R.Message s2]
psubscribe classes = getRedis >>= liftIO . flip R.psubscribe classes

punsubscribe :: (WithRedis m, BS s1, BS s2) => [s1] -> m [R.Message s2]
punsubscribe classes = getRedis >>= liftIO . flip R.punsubscribe classes

publish :: (WithRedis m, BS s1, BS s2) => s1 -> s2 -> m (R.Reply Int)
publish klass msg = do r <- getRedis
                       liftIO $ R.publish r klass msg

listen :: (WithRedis m, BS s) => Int -> m (Maybe (R.Message s))
listen timeout = getRedis >>= liftIO . flip R.listen timeout

save :: WithRedis m => m (R.Reply ())
save = getRedis >>= liftIO . R.save

bgsave :: WithRedis m => m (R.Reply ())
bgsave = getRedis >>= liftIO . R.bgsave

lastsave :: WithRedis m => m (R.Reply Int)
lastsave = getRedis >>= liftIO . R.lastsave

bgrewriteaof :: WithRedis m => m (R.Reply ())
bgrewriteaof = getRedis >>= liftIO . R.bgrewriteaof