    module Database.Redis (
        -- * How To Use This Module
        -- |
        -- Connect to a Redis server:
        --
        -- @
        -- -- connects to localhost:6379
        -- conn <- 'checkedConnect' 'defaultConnectInfo'
        -- @
        --
        -- Connect to a Redis server using TLS:
        --
        -- @
        -- -- connects to foobar.redis.cache.windows.net:6380
        -- import Network.TLS
        -- import Network.TLS.Extra.Cipher
        -- import Data.X509.CertificateStore
        -- import Data.Default.Class (def)
        -- (Just certStore) <- readCertificateStore "azure-redis.crt"
        -- let tlsParams = (defaultParamsClient "foobar.redis.cache.windows.net" "") { clientSupported = def { supportedCiphers = ciphersuite_strong }, clientShared = def { sharedCAStore = certStore } }
        -- let redisConnInfo = defaultConnectInfo { connectHost = "foobar.redis.cache.windows.net", connectPort = PortNumber 6380, connectTLSParams = Just tlsParams, connectAuth = Just "Foobar!" }
        -- conn <- checkedConnect redisConnInfo
        -- @
        --
        -- Send commands to the server:
        --
        -- @
        -- {-\# LANGUAGE OverloadedStrings \#-}
        -- ...
        -- 'runRedis' conn $ do
        --      'set' \"hello\" \"hello\"
        --      set \"world\" \"world\"
        --      hello <- 'get' \"hello\"
        --      world <- get \"world\"
        --      liftIO $ print (hello,world)
        -- @
        --
        -- disconnect all idle resources in the connection pool:
        --
        -- @
        -- 'disconnect' 'conn'
        -- @

        -- ** Command Type Signatures
        -- |Redis commands behave differently when issued in- or outside of a
        --  transaction. To make them work in both contexts, most command functions
        --  have a type signature similar to the following:
        --
        --  @
        --  'echo' :: ('RedisCtx' m f) => ByteString -> m (f ByteString)
        --  @
        --
        --  Here is how to interpret this type signature:
        --
        --  * The argument types are independent of the execution context. 'echo'
        --    always takes a 'ByteString' parameter, whether in- or outside of a
        --    transaction. This is true for all command functions.
        --
        --  * All Redis commands return their result wrapped in some \"container\".
        --    The type @f@ of this container depends on the commands execution
        --    context @m@. The 'ByteString' return type in the example is specific
        --    to the 'echo' command. For other commands, it will often be another
        --    type.
        --
        --  * In the \"normal\" context 'Redis', outside of any transactions,
        --    results are wrapped in an @'Either' 'Reply'@.
        --
        --  * Inside a transaction, in the 'RedisTx' context, results are wrapped in
        --    a 'Queued'.
        --
        --  In short, you can view any command with a 'RedisCtx' constraint in the
        --  type signature, to \"have two types\". For example 'echo' \"has both
        --  types\":
        --
        --  @
        --  echo :: ByteString -> Redis (Either Reply ByteString)
        --  echo :: ByteString -> RedisTx (Queued ByteString)
        --  @
        --
        --  [Exercise] What are the types of 'expire' inside a transaction and
        --    'lindex' outside of a transaction? The solutions are at the very
        --    bottom of this page.

        -- ** Lua Scripting
        -- |Lua values returned from the 'eval' and 'evalsha' functions will be
        --  converted to Haskell values by the 'decode' function from the
        --  'RedisResult' type class.
        --
        --  @
        --  Lua Type      | Haskell Type       | Conversion Example
        --  --------------|--------------------|-----------------------------
        --  Number        | Integer            | 1.23   => 1
        --  String        | ByteString, Double | \"1.23\" => \"1.23\" or 1.23
        --  Boolean       | Bool               | false  => False
        --  Table         | List               | {1,2}  => [1,2]
        --  @
        --
        --  Additionally, any of the Haskell types from the table above can be
        --  wrapped in a 'Maybe':
        --
        --  @
        --  42  => Just 42 :: Maybe Integer
        --  nil => Nothing :: Maybe Integer
        --  @
        --
        --  Note that Redis imposes some limitations on the possible conversions:
        --
        --  * Lua numbers can only be converted to Integers. Only Lua strings can be
        --    interpreted as Doubles.
        --
        --  * Associative Lua tables can not be converted at all. Returned tables
        --   must be \"arrays\", i.e. indexed only by integers.
        --
        --  The Redis Scripting website (<http://redis.io/commands/eval>)
        --  documents the exact semantics of the scripting commands and value
        --  conversion.

        -- ** Automatic Pipelining
        -- |Commands are automatically pipelined as much as possible. For example,
        --  in the above \"hello world\" example, all four commands are pipelined.
        --  Automatic pipelining makes use of Haskell's laziness. As long as a
        --  previous reply is not evaluated, subsequent commands can be pipelined.
        --
        --  Automatic pipelining is limited to the scope of 'runRedis' call and
        --  it is guaranteed that every reply expected as a part of 'runRedis'
        --  execution gets received after 'runRedis` invocation.
        --
        --  To keep memory usage low, the number of requests \"in the pipeline\" is
        --  limited (per connection) to 1000. After that number, the next command is
        --  sent only when at least one reply has been received. That means, command
        --  functions may block until there are less than 1000 outstanding replies.
        --

        -- ** Error Behavior
        -- |
        --  [Operations against keys holding the wrong kind of value:] Outside of a
        --    transaction, if the Redis server returns an 'Error', command functions
        --    will return 'Left' the 'Reply'. The library user can inspect the error
        --    message to gain  information on what kind of error occured.
        --
        --  [Connection to the server lost:] In case of a lost connection, command
        --    functions throw a 'ConnectionLostException'. It can only be caught
        --    outside of 'runRedis'.
        --
        --  [Trying to connect to an unreachable server:] When trying to connect to
        --    a server that does not exist or can't be reached, the connection pool
        --    only starts the first connection when actually executing a call to
        --    the server. This can lead to discovering very late that the server is
        --    not available, for example when running a server that logs to Redis.
        --    To prevent this, run a 'ping' command directly after connecting or
        --    use the 'checkedConnect' function which encapsulates this behavior.
        --
        --  [Exceptions:] Any exceptions can only be caught /outside/ of 'runRedis'.
        --    This way the connection pool can properly close the connection, making
        --    sure it is not left in an unusable state, e.g. closed or inside a
        --    transaction.
        --

        -- * The Redis Monad
        Redis(), runRedis,
        unRedis, reRedis,
        RedisCtx(..), MonadRedis(..),

        -- * Connection
        Connection, ConnectError(..), connect, checkedConnect, disconnect,
        ConnectInfo(..), defaultConnectInfo, parseConnectInfo,
        HostName, PortID(..),

        -- * Commands
        module Database.Redis.Commands,

        -- * Transactions
        module Database.Redis.Transactions,

        -- * Pub\/Sub
        module Database.Redis.PubSub,

        -- * Low-Level Command API
        sendRequest,
        Reply(..),Status(..),RedisResult(..),ConnectionLostException(..),

        -- |[Solution to Exercise]
        --
        --  Type of 'expire' inside a transaction:
        --
        --  > expire :: ByteString -> Integer -> RedisTx (Queued Bool)
        --
        --  Type of 'lindex' outside of a transaction:
        --
        --  > lindex :: ByteString -> Integer -> Redis (Either Reply ByteString)
        --
    ) where

    import Database.Redis.Core
    import Database.Redis.PubSub
    import Database.Redis.Protocol
    import Database.Redis.ProtocolPipelining
        (HostName, PortID(..), ConnectionLostException(..))
    import Database.Redis.Transactions
    import Database.Redis.Types
    import Database.Redis.URL

    import Database.Redis.Commands