How would you implement the reverse state monad in OCaml? (Since it relies heavily on laziness, I guess one has to use the Lazy module from the standard library).
Asked
Active
Viewed 390 times
9
-
How about the [Tardis monad](http://hackage.haskell.org/package/tardis-0.3.0.0/docs/Control-Monad-Tardis.html), too? Excellent for calculating [bowling](http://unknownparallel.wordpress.com/2012/11/05/bowling-on-a-tardis/) scores, among other things. – Boyd Stephen Smith Jr. Jul 24 '14 at 21:15
-
@BoydStephenSmithJr.: Are you asking another question? – Bob Jul 24 '14 at 21:17
-
I suppose, yes. But, I'll wait for the answer on this one before I post mine. – Boyd Stephen Smith Jr. Jul 24 '14 at 22:06
-
1@Bob, what have you tried? I can tell you that implementing it using the Lazy module works out pretty much the way you would expect. Is confirmation of existence all you're looking for, or do you have a more specific question? – Lambdageek Jul 24 '14 at 22:13
-
@Lambdageek: my question is "how do you do that without looping in OCaml?". Wherever I put laziness, I always end up with recursion looping. It would be great if you could write an answer. – Bob Jul 24 '14 at 22:19
1 Answers
7
I put up a Gist of a solution.
The tricky bit is:
type 's l = 's Lazy.t
type ('a, 's) st = 's -> ('a * 's l)
let bind (mx : ('a, 's) st) (f : ('a -> ('b, 's) st)) (s : 's l) : ('b * 's l) =
(* conceptually we want
let rec (lazy (y, s'')) = lazy (mx s')
and (lazy (z, s')) = lazy (f y s)
in (force z, s'')
but that's not legal Caml.
So instead we get back lazy pairs of type ('a * 's l) l and we
lazily project out the pieces that we need.
*)
let rec ys'' = lazy (mx (LazyUtils.join (LazyUtils.snd zs')))
and (zs' : ('b * 's l) l) = lazy (f (Lazy.force (LazyUtils.fst ys'')) s)
in (Lazy.force (LazyUtils.fst zs'), LazyUtils.join (LazyUtils.snd ys''))
As I mentioned in the comment, the somewhat tricky bit is that you don't want to accidentally force the computation of the state too soon. Unfortunately to get the mutual recursion right, you're also forced to temporarily make the computation's answers (which are flowing forward) lazy as well. So the basic rule of thumbs are:
- Do what the types tell you to do.
- Never force the state except under a
lazy e
construct.
In particular, LazyUtils.join : 'a Lazy.t Lazy.t -> 'a Lazy.t
cannot be:
let join xll = Lazy.force xll
Because that would force the outer thunk too early. Instead it must be:
let join xll = lazy (Lazy.force (Lazy.force xll))
Which looks like stuttering, but in fact correctly delays all computation.

Lambdageek
- 12,465
- 1
- 25
- 33
-
Thank you very much for this great answer. I was able to rediscover your answer by myself after studying your tricks. Then I spent the rest of the day trying to generalize this to the case of the reverse state monad *transformer*. But I am stuck: I cannot define the bind. The only solution that I can vaguely imagine would use the mfix of the monad in parameter but this is itself problematic to define because it leads to looping recursion. That's why I ask [this other question](https://stackoverflow.com/questions/24962267/the-reverse-state-monad-transformer-in-ocaml). Any idea? – Bob Jul 25 '14 at 18:34