Copyright | Copyright 2022 Shea Levy. |
---|---|
License | Apache-2.0 |
Maintainer | shea@shealevy.com |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
A domain-specific language for modifying the behavior of EventBackend
s, needed when
the caller can't specify the EventBackend
to use directly.
The instrumented capability problem
A common approach for polymorphic effect management in Haskell is the "capability pattern",
where a function polymorphic in some monad m
takes as an argument a value also polymorphic
in m
that can be used to run a constrained set of effects in m
. One example of such a
"capability" type would be EventBackend
, which for example enables running
newEvent
in whatever m
it's instantiated in. These capabilities are often
themselves implemented in terms of other capabilities, and are ultimately concretely
instantiated in some base monad (typically IO
, or perhaps ST
for a pure
mock) and then hoist
ed to the application's monadic context (e.g. hoistEventBackend
).
Normally this compose + hoist approach works fine, since any capabilities that are dependencies of the
the capability we're hoisting are hidden in its closure. But if a capability depends on an EventBackend
for instrumentation, closing over it at creation time causes a problem: at the call-site of the various
effects enabled by the capability, we have no way to modify the EventBackend
to e.g. be a noop (because
we don't need the details of this effect's actions to instrument the calling function effectively) or to
have its Event
s descend from some current Event
. Thus, the DSL defined
in this module: effects which take some polymorphic capability can *also* take an EventBackendModifier
and the capability can modify its captured EventBackend
with modifyEventBackend
accordingly.
An alternative would be to have each effect in the capability take an EventBackend
at the call site.
This would foreclose hoist
ing along an arbitrary natural transformation, since the EventBackend
would
be in negative position, but constrained hoist
ing might be possible with MonadUnliftIO
or MonadUnlift
or MonadBaseControl
if we share a base monad, or if we implemented EventBackend
s in a separate base monad
that appears in the type of our capabilities and ensure it's liftable to both our application monad and the
capability's base instantiation.
Synopsis
- data EventBackendModifier r r' where
- Silence :: forall r. EventBackendModifier r ()
- SetAncestor :: forall r. r -> EventBackendModifier r r
- SetInitialCause :: forall r. r -> EventBackendModifier r r
- data EventBackendModifiers r r'
- modifyEventBackend :: Monad m => EventBackendModifiers r r' -> EventBackend m r s -> EventBackend m r' s
- unmodified :: EventBackendModifiers r r
- silence :: EventBackendModifiers r ()
- setAncestor :: r -> EventBackendModifiers r r
- setInitialCause :: r -> EventBackendModifiers r r
Documentation
data EventBackendModifier r r' where Source #
Modify an EventBackend
, chaging its reference type from r
to r'
Silence :: forall r. EventBackendModifier r () | Ignore all instrumentation using the |
SetAncestor | Mark every parentless event as the child of a known |
| |
SetInitialCause | Mark every causeless event as proximately caused by a known |
|
data EventBackendModifiers r r' Source #
A sequence of EventBackendModifier
s
The free Category
over EventBackendModifier
Instances
Category EventBackendModifiers Source # | |
Defined in Observe.Event.BackendModification id :: forall (a :: k). EventBackendModifiers a a # (.) :: forall (b :: k) (c :: k) (a :: k). EventBackendModifiers b c -> EventBackendModifiers a b -> EventBackendModifiers a c # |
modifyEventBackend :: Monad m => EventBackendModifiers r r' -> EventBackend m r s -> EventBackend m r' s Source #
Modify an EventBackend
according to the given EventBackendModifiers
.
This is a right fold, e.g. modifyEventBackend (a . b . id) backend
first
modifies backend
with b
and then modifies the result with a
.
Simple EventBackendModifiers
unmodified :: EventBackendModifiers r r Source #
An EventBackendModifiers
that does nothing.
silence :: EventBackendModifiers r () Source #
An EventBackendModifiers
that silences events.
setAncestor :: r -> EventBackendModifiers r r Source #
An EventBackendModifiers
that marks every parentless event as the child
of a known Event
.
setInitialCause :: r -> EventBackendModifiers r r Source #
An EventBackendModifiers
that marks every causeless event as proximately caused
by a known Event
.