(Related question Select instance behavior at runtime)
I want to define a kind of backend (reusable as independent api), then provide multiple implementations and be able to select one at runtime.
My application will be like
data AppData =
AppData { ...
, backend :: Backend
}
for simplicity, let our backend (conceptual code)
data Backend key value =
Backend { get :: [(key, value)]
, cud :: key -> Maybe value -> () -- Create Update Delete
}
now What's the proper/recommended way to define our Backend
type?
I think it will be monadic (then Monad m
) but also IO
(then MonadIO m
) but also pure (then we need change -> ()
by -> Backend key value
) and so on...
I'm doubting, the next attempt works monadic, IO
and pure, but may be over-engineering
data Backend m k v =
Backend { get :: MonadIO m => m [(k, v)]
, cud :: MonadIO m => k -> Maybe v -> Backend m k v
}
the MonadIO
is a strong restriction and return immutable version on cud
is redundant (in most cases?) with m
.
What is the proper/recommended way to abstract it?
Thank you!
Once defined our Backend
API my intention was use as (conceptual code) more or less
main = do
configuration <- getAppConfiguration
selectedBackend <- case preferedBackend configuration of
"mongoDB" -> MongoDBBackend.makeBackend
"sqlite" -> SqliteBackend.makeBackend
"volatile" -> PureDataMapBackend.makeBackend
...
appData <- makeAppData configuration selectedBackend
....