Search code examples
ocamlmonadsmonad-transformers

Using monad transformers in OCaml


I'm getting myself familiar with monad transformers in OCaml using monads library.

Here is an example I'm working with:

open Base
open Stdio
open Monads.Std

module St = struct
  include Monad.State.T1 (Monoid.Int) (Monad.Ident)
  include Monad.State.Make (Monoid.Int) (Monad.Ident)
end

module W = Monad.Writer.Make (Monoid.String) (St)

let w_example =
  let open W in
  let writer =
    let* () = write "A" in
    let* () = lift (St.put 42) in
    return (-1)
  in
  let w_result = run writer in
  let s_result, state = St.run w_result 0 in
  s_result, state

let () = printf "((%d, %s), %d)\n" (fst (fst w_example)) (snd (fst w_example)) (snd w_example)

I have two questions:

  1. Is there an automatic way to retrieve the result -1, the log A, and the state 42 all at once without manually run-ing all the composed monads inwards?
  2. To put a new state into W monad, I needed to separate St module in order to expose St.put. W does not have put exposed. Is there a way if I just went with
module W =
  Monad.Writer.Make
    (Monoid.String)
    (struct
      include Monad.State.T1 (Monoid.Int) (Monad.Ident)
      include Monad.State.Make (Monoid.Int) (Monad.Ident)
    end)

without a separate St?


Solution

    1. You can define a run function that goes through all the layer of the monad stack:
    let run x st = St.run (W.run x) st
    

    It is also a good place to define a more human-friendly return type like

    type 'a t = { int_st : int;  string_st : string; return : 'a }
    let run x st =
      let ((return, string_st), int_st) = St.run (W.run x) st in
      { int_st; string_st; return }
    
    1. You need to construct Monad.State.Make (Monoid.Int) (Monad.Ident) before using its put function anyway. Trying to avoid naming the St monad would be mostly unpractical.