I'm playing around with implementing a Redis client-library in Haskell and it is my goal to encode, as much as possible, the semantics of the Redis commands in Haskell's type system. Redis, for those who don't know, is a datastore, accessed over the network. I will use it to exemplify my problem, but Redis is not the focus of this question.

An Example Function

Consider the function

get :: (RedisValue a) => Key -> Redis a
get k = decodeValue <$> sendCommand ["GET", key]

It sends a command to the datastore and returns a value stored under the given Key (for this example, you can consider type Key = String). As for the return-type:

  • Redis is an instance of Monad and MonadIO. It encapsulates information about the network connection. sendCommand sends the request and returns the datastore's reply.

  • a is polymorphic, for example either Strings or ByteStrings can be returned, depending on the context.

The following code should clarify the text above.

data Redis a = ...

instance MonadIO Redis where ...
instance Monad Redis where ...

sendCommand :: [String] -> Redis String

class RedisValue a where
    decodeValue :: String -> a

-- example instances
instance RedisValue String where ...
instance RedisValue ByteString where ...

Different Context, Different Types

Redis supports a simple form of transactions. In a transaction most commands can be sent the same as outside of a transaction. However their execution is delayed until the user sends the commit command (which is called exec in Redis). Inside the transaction, the datastore only returns an acknowledgment that the command is stored for later execution. Upon commit (exec) all results of all stored commands are retur

Edit
Report