Copyright | (c) Michael Szvetits 2020 |
---|---|
License | BSD3 (see the file LICENSE) |
Maintainer | typedbyte@qualified.name |
Stability | stable |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
This module provides TemplateHaskell
functions to generate the handling,
lifting and tagging infrastructure for effect type classes.
Synopsis
- makeEffect :: Name -> Q [Dec]
- makeHandler :: Name -> Q [Dec]
- makeFinder :: Name -> Q [Dec]
- makeLifter :: Name -> Q [Dec]
- makeTaggedEffect :: Name -> Q [Dec]
- makeTaggedEffectWith :: (String -> Q String) -> Name -> Q [Dec]
- makeTagger :: Name -> Q [Dec]
- makeTaggerWith :: (String -> Q String) -> Name -> Q [Dec]
- liftL :: EachVia effs t m a -> EachVia (eff ': effs) t m a
- runL :: EachVia (eff ': effs) t m a -> EachVia effs t m a
- removeApostrophe :: String -> Q String
Common Generators
makeEffect :: Name -> Q [Dec] Source #
Generates the effect handling and lifting infrastructure for an effect which
does not have a tag type parameter. Requires the TemplateHaskell
language
extension.
Consider the following effect type class:
class Monad
m => MyEffect a b c m where
...
makeEffect ''MyEffect
then generates three instances for this effect type
class (Lift
for first-order effects, Control
for higher-order effects):
instanceHandle
(MyEffect a b c) t m => MyEffect a b c (EachVia
(MyEffect a b c : effs) t m) where ... instance {-# OVERLAPPABLE #-}Find
(MyEffect a b c) effs t m => MyEffect a b c (EachVia
(other : effs) t m) where ... instance 'Lift'/'Control' (MyEffect a b c) t m => MyEffect a b c (EachVia
'[] t m) where ...
The first instance indicates that MyEffect
was found at the head of the type
level list of effects to be handled, so MyEffect
is delegated to t
.
The second instance indicates that MyEffect
was not found at the head of the
type level list of effects to be handled, so we must find MyEffect
in the tail effs
of the type level list.
The third instance indicates that MyEffect
could not be found in the type level
list of effects to be handled, so the effect must be delegated further down the monad
transformer stack in order to find its corresponding effect handler.
Without TemplateHaskell
, you have to write these three instances by hand. These
instances can also be generated separately, see makeHandler
, makeFinder
and
makeLifter
.
makeHandler :: Name -> Q [Dec] Source #
Similar to makeEffect
, but only generates the effect type class instance
for handling an effect.
makeFinder :: Name -> Q [Dec] Source #
Similar to makeEffect
, but only generates the effect type class instance
for finding the effect in the tail of the type level list.
Since: 0.2.0.0
makeLifter :: Name -> Q [Dec] Source #
Similar to makeEffect
, but only generates the effect type class instance
for lifting an effect.
Tag-based Generators
makeTaggedEffect :: Name -> Q [Dec] Source #
Generates the effect handling and lifting infrastructure for an effect which
has a tag type parameter. It is expected that the tag type parameter is the first
type parameter of the effect type class. It is also expected that the names of the
effect type class and its methods end with an apostrophe "'". If you want more
control over the naming convention, use makeTaggedEffectWith
.
In general, this function generates everything that makeEffect
does, but also some
additional things. Consider the following effect type class:
class Monad
m => MyEffect' tag a b c m where
methodA' :: a -> m ()
methodB' :: b -> m ()
methodC' :: c -> m ()
then generates the following additional things:makeTaggedEffect
''MyEffect'
- A type synonym for the untagged version of
MyEffect'
with the nameMyEffect
(note the missing apostrophe). - Untagged versions of the effect methods, namely
methodA
,methodB
andmethodC
(note the missing apostrophes). - An instance of
MyEffect'
for the typeTagger
which does not handle the effect, but simply tags, retags or untags theMyEffect'
constraint of a computation. - Three functions
tagMyEffect'
,retagMyEffect'
anduntagMyEffect'
which make use of theTagger
instance.
As a rule of thumb, whenever you see an apostrophe suffix in the name of a definition
somewhere in this library, it has something to do with tags. Most of the time you
will use such definitions in combination with the language extension TypeApplications
,
like:
... forall tag ... methodA' @tag ... tagMyEffect' @"newTag" program retagMyEffect' @"oldTag" @"newTag" program untagMyEffect' @"erasedTag" program
All the tag-related definitions can also be generated separately (i.e., without the
instances generated by makeEffect
), see makeTagger
and makeTaggerWith
.
makeTaggedEffectWith :: (String -> Q String) -> Name -> Q [Dec] Source #
Similar to makeTaggedEffect
, but allows to define a naming convention function
for the names of the effect type class and its methods. This function is used to
transform the name of a tagged definition (i.e., the type class or its methods) into
its untagged counterpart.
The default naming convention is enforced by removeApostrophe
, which simply
removes the apostrophe "'" at the end of a name.
makeTagger :: Name -> Q [Dec] Source #
Similar to makeTaggedEffect
, but only generates the tag-related definitions.
makeTaggerWith :: (String -> Q String) -> Name -> Q [Dec] Source #
Similar to makeTaggedEffectWith
, but only generates the tag-related definitions.
Lifting Convenience
liftL :: EachVia effs t m a -> EachVia (eff ': effs) t m a Source #
Adds an effect eff
to the type level list of effects that need to be
handled by the transformer t
. From a structural point of view, this is
analogous to lift
in the mtl
ecosystem. This function comes in handy
when writing the Find
-based instance of an effect by hand.
Since: 0.2.0.0
runL :: EachVia (eff ': effs) t m a -> EachVia effs t m a Source #
Removes an effect eff
from the type level list of effects that need to be
handled by the transformer t
. From a structural point of view, this is
analogous to the run...
functions in the mtl
ecosystem. This function
comes in handy when writing the Find
-based instance of an effect by hand.
Since: 0.2.0.0