can-i-haz

Generic implementation of the Has and CoHas patterns

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

Version on this page:0.3.1.0
LTS Haskell 23.0:0.3.1.1
Stackage Nightly 2024-12-09:0.3.1.1
Latest on Hackage:0.3.1.1

See all snapshots can-i-haz appears in

BSD-3-Clause licensed by Georg Rudoy
Maintained by [email protected]
This version can be pinned in stack with:can-i-haz-0.3.1.0@sha256:63da875beeabf4ed382b78c6e114f20e924a87397260f25eb8d057a7bb157338,1537

Module documentation for 0.3.1.0

Depends on 2 packages(full list with versions):

can-i-haz

Build Status Hackage Stackage LTS 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:

data DbConfig = DbConfig { .. }
data WebConfig = WebConfig { .. }

as well as a single type containing both of those:

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:

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.

Acknowledgements

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

Changes

Changelog for can-i-haz

0.3.1.0

  • Add liftMaybe utility function to Control.Monad.Except.CoHas.

0.3.0.0

  • Reexport Control.Monad.Reader from the Control.Monad.Reader.Has module with custom ask, asks and reader functions more compatible with the Has class.
  • Similarly for Control.Monad.Except, CoHas and throwError/liftError functions.

0.2.1.0

  • Added the update method to Has (yay lenses).
  • Fixed the documentation regarding the recursiveness of the search.

0.2.0.1

  • Less boilerplate for Has tuple instances and CoHas Either instance.
  • Out-of-the-box Has support for up to sextuples due to the reduced boilerplate.

0.2.0.0

  • Added CoHas class (dual to Has), allowing injecting values into sum types.

0.1.0.1

  • Added documentation.
  • Export the SearchSuccessful type which might aid hand-writing instances.

0.1.0.0

Initial release.