# Abstract Key Value Store

I'm attempting to define an abstract interface for a key value store.

Some requirements:

• Once a key has been written it should never be overwritten (although it may be deleted
• Transactions with multiple operations are not required

To begin with, this is the basic interface:

class KVS a where
get :: ByteString -> a -> Maybe ByteString
put :: ByteString -> ByteString -> a -> Maybe a
delete :: ByteString -> a -> a


And here's an InMemory instance:

newtype InMemoryKVS = InMemoryKVS (M.Map ByteString ByteString)

instance KVS InMemoryKVS where
get k (InMemoryKVS m) = M.lookup k m
put k v (InMemoryKVS m)
| M.Member k m = Nothing
| otherwise = Just $InMemoryKVS$ M.insert k v m
delete k (InMemoryKVS m) = InMemoryKVS $M.delete k m  I would also like to write instances that use the IO monad, so presumably the typeclass needs to take this into account. Here's my first attempt at that: class IOKVS a where ioGet :: ByteString -> a -> IO (Maybe ByteString) ioPut :: ByteString -> ByteString -> a -> IO (Maybe a) ioDelete :: ByteString -> a -> IO a instance IOKVS InMemoryKVS where ioGet k s = return$ get k s
ioPut k v s = return $put k v s ioDelete k s = return$ delete k s


Here is how I'm currently using it:

{-# LANGUAGE OverloadedStrings #-}
main :: IO()
main = do
kvs <- return (InMemoryKVS M.empty)
kvs' <- ioPut "foo" "bar" kvs
maybe (putStrLn "put failed") (printKey "foo") kvs'

printKey :: IOKVS a => B.ByteString -> a -> IO()
printKey k s = do
v <- ioGet k s
maybe (putStrLn "doesn't exist") (\v -> B.putStrLn v) v


I feel that there is a lot of room for improvement with my design. Here are some questions that I have:

1. In main I'm manually keeping track of the state of kvs. Could my code be simplified by using the State monad? If so how can I manage the complexity of having IO, State and Maybe stacked?
2. I'm not happy with the design of the IOKVS typeclass. Specifically I don't think it will work well with concurrent access.
3. Can this design hide a connection pool to an external db?
4. Is there a more elegant way to handle the failure cases then using the maybe function? I would like to be able to compose these into higher level actions that may fail, but not have to deal with the failure case within each action.
5. Can you see any other areas for improvement?