Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/0xd34df00d/can-i-haz

Generic implementation of the Has pattern for MonadReader/MonadState and CoHas for MonadError
https://github.com/0xd34df00d/can-i-haz

generics haskell reader-monad state-monad

Last synced: 2 months ago
JSON representation

Generic implementation of the Has pattern for MonadReader/MonadState and CoHas for MonadError

Awesome Lists containing this project

README

        

# can-i-haz

[![Build Status][travis-badge]][travis]
[![Hackage][hackage-badge]][hackage]
[![Stackage LTS][stackage-lts-badge]][stackage-lts]
[![Stackage Nightly][stackage-nightly-badge]][stackage-nightly]

`Generic` implementation of the Has-pattern (mostly useful with `MonadReader` and `MonadState`)
and its dual `CoHas` (mostly useful with `MonadError`).

## Motivation

Assume there are two types representing the `MonadReader` environments
for different parts of an app:
```haskell
data DbConfig = DbConfig { .. }
data WebConfig = WebConfig { .. }
```
as well as a single type containing both of those:
```haskell
data AppEnv = AppEnv
{ dbConfig :: DbConfig
, webConfig :: WebConfig
}
```

What should be the `MonadReader` constraint of the DB module and web module respectively?
1. It could be `MonadReader AppEnv m` for both, introducing unnecessary coupling.
2. Or it could be `MonadReader DbConfig m` for the DB module
and `MonadReader WebConfig m` for the web module respectively,
but combining them becomes a pain.

Or, it could be `MonadReader r m, Has DbConfig r` for the DB module,
where `Has` class allows projecting `DbConfig` out of some `r`,
and similarly for the web module!
This approach keeps both modules decoupled, while allowing using them in the same monad stack.

The only downside is that now one has to define the `Has` class
and write tediuos instances for the `AppEnv` type
(and potentially other types in case of tests).

## The solution

This library saves you from this unnecessary boilerplate!
The only thing you have to do is to append the `deriving`-clause:
```haskell
data AppEnv = AppEnv
{ dbConfig :: DbConfig
, webConfig :: WebConfig
} deriving (Generic, Has DbConfig, Has WebConfig)
```
and use `ask extract` instead of `ask` (but this is something you'd have to do anyway).

## Reversing the arrows: `CoHas`

There is a dual (but arguably less frequent) problem of combining different parts of an application
living in different `MonadError` environments.
The duality is due to us now wanting to _inject_ values of a type _into_ a "wider" _sum_ type
(as opposed to _projecting_ values _out_ of some _product_ type).
The `CoHas` class serves exactly this purpose.

## Documentation

Perhaps the best source is the [Haddock docs](http://hackage.haskell.org/package/can-i-haz/docs/Control-Monad-Reader-Has.html).

## Acknowledgements

Thanks lyxia @ #haskell for the type families-based derivation of the `GHas` instance.

[travis]:
[travis-badge]:
[hackage]:
[hackage-badge]:
[stackage-lts-badge]:
[stackage-nightly-badge]:
[stackage-lts]:
[stackage-nightly]: