A class for monads which provide for the ability to account for all
possible exit points from a computation, and to mask asynchronous
exceptions. Continuation-based monads are invalid instances of this
class.
Instances should ensure that, in the following code:
fg = f `finally` g
The action
g is called regardless of what occurs within
f, including async exceptions. Some monads allow
f
to abort the computation via other effects than throwing an exception.
For simplicity, we will consider aborting and throwing an exception to
be two forms of "throwing an error".
If
f and
g both throw an error, the error thrown by
fg depends on which errors we're talking about. In a monad
transformer stack, the deeper layers override the effects of the inner
layers; for example,
ExceptT e1 (Except e2) a represents a
value of type
Either e2 (Either e1 a), so throwing both an
e1 and an
e2 will result in
Left e2. If
f and
g both throw an error from the same layer,
instances should ensure that the error from
g wins.
Effects other than throwing an error are also overridden by the deeper
layers. For example,
StateT s Maybe a represents a value of
type
s -> Maybe (a, s), so if an error thrown from
f causes this function to return
Nothing, any
changes to the state which
f also performed will be erased.
As a result,
g will see the state as it was before
f. Once
g completes,
f's error will be
rethrown, so
g' state changes will be erased as well. This is
the normal interaction between effects in a monad transformer stack.
By contrast,
lifted-base's version of
finally always
discards all of
g's non-IO effects, and
g never sees
any of
f's non-IO effects, regardless of the layer ordering
and regardless of whether
f throws an error. This is not the
result of interacting effects, but a consequence of
MonadBaseControl's approach.