haskellmonad-transformers

Monad transformer loop failing when the short circuit happens on the first run


I have a RWST in haskell over a Maybe, which short circuits under certain conditions. I want to run this action until it returns Nothing, and get the last Just. So my situation is as follows;

type St = MyState
type Re = MyRead
type Wr = MyWriter
type Foo = RWST Re Wr St Maybe

action :: Foo ()

The only way that I thought of for achieving that, is by defining the following;

run = action >> run <|> action

which has the downside that if the short circuiting happens on the first second run, even though there is an output, I get Nothing because run runs the action twice. Is there any better way of looping? I looked into extra but the functions supplied there do not help my cause.


So, after considering the answers given, and trying everything out, it seems the easiest solution is to use many. In the general case though, swapping for a MaybeT as suggested is a better solution.


Solution

  • I've been looking over your question and going back and forth about whether this answer applies to your situation. Really I can't tell without more context, so I'll just leave it here in case.

    You are using Maybe as the base monad, and that might not be what you want. I want to forget about the reader and writer part of RWST for a moment and pretend we are just using StateT, because it simpler, but the same idea applies (StateT is basically the same thing as RWST () () after all). The definition of StateT is:

    newtype StateT s m a = StateT (s -> m (a, s))
    

    So, after all is unrolled and done, the type of run ends up as

    run :: St -> Maybe ((), St)
    

    You give it an St, and it gives you back either a pair with a resulting St in it, or Nothing. In particular, when it "fails" (as in, returns Nothing), the failure does not come with a resulting state, so all state changes prior to the failure are forgotten. That means if you ever take the right side of (<|>), anything that happened in the left side doesn't matter anymore, the only influence it has on the result is the fact that it failed. But the fact that you want to do this in a loop betrays that you want to keep the state changes after failure.

    That is what MaybeT is for. Put the MaybeT on the outside. MaybeT is defined as

     newtype MaybeT m a = MaybeT (m (Maybe a))
    

    So if your monad were MaybeT (State St), the implementation type would be

     run :: St -> (Maybe (), St)
    

    Notice that now, whether the action succeeds or fails, we get an St out the other end. This will allow (<|>) to carry on manipulating the state that its left side changed rather than starting anew, which I'm guessing is your intention (though I can't say for sure).

    In general, the inner monads of a transformer chain are more powerful -- a transformer are always subject to the rules of their inner monad. The "primary" effect of StateT s Maybe is that it either succeeds with a value or fails outright, and then the statefulness has to be "emulated" on top of that. Whereas the primary effect of MaybeT (State s) is that of statefulness -- no matter what MaybeT does, there is always a single state carried through the computation. More on transformer stack design here.