I am attempting to define an API to express a particular type of procedure in my program.
newtype Procedure a = { runProcedure :: ? }
There is
Let's pretend that rather than using State
/StateT
to store your procedures' state, you were using an IORef
in the IO
monad.
A priori there are two ways you could want mzero
(or fail
) to behave in a combination of the IO
and Maybe
monads:
mzero
wipes out the entire computation, so that mzero <|> x = x
; ormzero
causes the current computation to not return a value, but IO
-type effects are preserved.It sounds like you want the first one, so that the state set by one procedure is "unrolled" for the next procedure in a chain of <|>
s.
Of course, this semantics is impossible to implement. We don't know whether a computation will invoke mzero
until we run it, but doing so may have arbitrary IO
effects like launchTheMissiles
, which we can't roll back.
Now, let's try to build two different monad transformer stacks out of Maybe
and IO
:
IOT Maybe
-- oops, this doesn't exist!MaybeT IO
The one that exists (MaybeT IO
) gives the mzero
behavior that is possible, and the nonexistent IOT Maybe
corresponds to the other behavior.
Fortunately you're using State ProcedureState
, whose effects can be rolled back, rather than IO
; the monad transformer stack you want is the StateT ProcedureState Maybe
one.