I am trying to find the most elegant way of converting the following stateful imperative piece of code to pure functional representation (preferably in Haskell to use abstraction that its Monad implementation offers). However I am not yet good at combining different monads using transformers and the like. It seems to me, that analyzing other's takes on such tasks helps the best when learning how to do it myself. The imperative code:
while (true) {
while (x = get()) { // Think of this as returning Maybe something
put1(x) // may exit and present some failure representation
}
put2() // may exit and present some success representation
}
When get
returns Nothing
we need the execution to continue with put2
, when get
returns Just x
we want the x
to get passed to put1
and short-circuit only if put1
fails or loop otherwise. Basically put1
and put2
may terminate the whole thing or move to the following statement changing the underlying state somehow. get
can either succeed and invoke put1
and loop or fail and continue to put2
.
My idea was something along:
forever $ do
forever (get >>= put1)
put2
And why I was looking for something like that is because (get >>= put1)
could simply short-circuit whenever get
has nothing to return or put1
terminates. Similarly put2
terminates the outer loop. However I am not sure how to mix the State
with the necessary Maybe
and/or Either
to achieve this.
I think using transformers to combine State
and the other monads is necessary and thus the code will most probably not be that succint. But I guess it as well might not be much worse.
Any suggestion how to achieve the translation elegantly is welcome. This differs from "Stateful loop with different types of breaks" in avoiding explicit control-flow using if
, when
, while
and rather tries to encourage use of Maybe
, Either
, or some other handy >>=
semantics. Also there is always a straight-forward way how to translate the code into a functional one, however it can hardly be considered elegant.
EitherT
orMaybeT
and so you'll want to look into monad transformer stacks. Or! If you just want to explore, take a look at this monad:data M e s a = M { runM :: e -> Either e (s, a) }
noting that it instantiatesMonadState s
. – J. Abrahamsonexit()
. Can you how to do the functional equivalent of that now? – BergiTrue
,False
butMaybe something
giving rise to a different chaining. Theput1
andput2
do not seem to offer an easy way of being rewritten to not cause the main function to return. The answers to the original question did involve a lot of if-then-else like code, this is an attempt to use>>=
ofMaybe
andEither
instead. – jakubdanielexit
or especiallyerror
). That will give you a better sense of what the pretty abstractions you want actually need to support. – dfeuer