| Stability | experimental |
|---|---|
| Safe Haskell | Safe |
| Language | Haskell2010 |
Control.Monad.Except.CoHas
Description
This module defines a class CoHas intended to be used with the MonadError class
(and similar ones) or 'Control.Monad.Except.Except'/'Control.Monad.ExceptT' types.
The problem
Assume there are several types representing the possible errors in different parts of an application:
data DbError = ... data WebUIError = ...
as well as a single sum type containing all of those:
data AppError = AppDbError DbError | AppWebUIError WebUIError
What should be the MonadError constraint of the DB module and web module respectively?
- It could be
MonadError AppError mfor both, introducing unnecessary coupling. - Or it could be
MonadError DbError mfor the DB module andMonadError WebError mfor the web module respectively, but combining them becomes a pain.
Or, it could be MonadError e m, CoHas AppError e for the DB module (and similarly for the web module),
where some appropriately defined CoHas option sum class allows injecting option
creating a value of the sum type.
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 CoHas class
and write tedious instances for the AppError type (and potentially other types in case of, for example, tests).
But why bother doing the work that the machine will happily do for you?
The solution
This module defines the generic CoHas class as well as hides all the boilerplate behind GHC.Generics,
so all you have to do is to add the corresponding deriving-clause:
data AppError = AppDbError DbError | AppWebUIError WebUIError deriving (Generic, CoHas DbError, CoHas WebUIError)
and use throwError . inject instead of throwError (but this is something you'd have to do anyway).
Type safety
What should happen if sum does not have any way to construct it from option at all?
Of course, this means that we cannot inject option into sum, and no CoHas instance can be derived at all.
Indeed, this library will refuse to generate an instance in this case.
On the other hand, what should happen if sum contains multiple values of type option
(like Either option option), perhaps on different levels of nesting?
While technically we could make an arbitrary choice, like taking the first one in breadth-first or depth-first order,
we instead decide that such a choice is inherently ambiguous,
so this library will refuse to generate an instance in this case as well.
Documentation
class CoHas option sum where Source #
The CoHas option sum class is used for sum types that could be created from a value of type option.
Minimal complete definition
Nothing
Methods
inject :: option -> sum Source #
Inject an option into the sum type.
The default implementation searches sum for some constructor
that's compatible with option (potentially recursively) and creates sum using that constructor.
The default implementation typechecks if and only if there is a single matching constructor.
inject :: forall path. (Generic sum, SuccessfulSearch option sum path) => option -> sum Source #
Inject an option into the sum type.
The default implementation searches sum for some constructor
that's compatible with option (potentially recursively) and creates sum using that constructor.
The default implementation typechecks if and only if there is a single matching constructor.
Instances
| CoHas sum sum Source # | Each type can be injected into itself (and that is an |
Defined in Control.Monad.Except.CoHas | |
| SuccessfulSearch r (Either l r) path => CoHas r (Either l r) Source # | |
Defined in Control.Monad.Except.CoHas | |
| SuccessfulSearch l (Either l r) path => CoHas l (Either l r) Source # | |
Defined in Control.Monad.Except.CoHas | |