title | date |
---|---|
The `Has` pattern |
2020-05-06 |
The Has
pattern is used to access configuration or state in Layer 3 pure functions (see <fca6b335?cf>). The class itself is defined like this in the three-layer app:
class Has field env where
obtain :: env -> field
instance Has a a where
obtain = id
grab :: forall field env m. (MonadReader env m, Has field env) => m field
grab = asks $ obtain @field
Begin by creating an constraint alias. We will use MonadReader
as it is the most convenient, and also used by the utility grab
function defined above. This particular example is for passing database connections around:
type WithDb env m = (MonadReader env m, Has Connection env, MonadIO m)
-- An helper to work with the constraint
withDb :: WithDb env m => (Connection -> IO b) -> m b
withDb f = do
conn <- grab @Connection
liftIO $ f conn
Then any application-level function which queries on the database would use it as:
getUsers :: WithDb env m => m [User]
getUsers = undefined
To tie them all together, you would use runReaderT
from the suitable top-level function:
users <- flip runReaderT conn getUsers