Skip to content

Instantly share code, notes, and snippets.

@eatonphil
Created June 7, 2016 20:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save eatonphil/20927ccf9f59c6b47c48002e147aa45c to your computer and use it in GitHub Desktop.
Save eatonphil/20927ccf9f59c6b47c48002e147aa45c to your computer and use it in GitHub Desktop.
Monads in ocaml

Monads

I finally grasped the use of monads while building linux images. To build the image, there were a series of steps that needed to run as root. If any step failed, the entire process needed to stop immediately or risk damaging my own filesystem. My script began looking like this:

res1 = execOrFail(cmd1, args)

res2 = execOrFail(cmd2, res1)

...

resn-1 = execOrFail(cmdn-1, resn-2)

finally(cmdn, resn-1)

It was at this point that I realized these steps could be more easily represented contained within a monad. Using a stop-on-failure-monad, this script would look more like this:

let open FailMonad in
monadicExec(cmd1, args)  >>= fun res1
monadicExec(cmd2, res1)     >>= fun res2
...
monadicExec(cmdn-1, resn-2) >>= fun resn-1
return monadicFinally(cmdn, resn-1)

I still didn't have a good understanding of what monads were though, so I decided to start with the Maybe monad. Running this example (ocaml monad.ml) will produce no output because the assertions do not fail. So feel free to study the source to come to a similar understanding.

In the future, I plan to apply this knowledge to create a stop-on-failure-monad and the appropriate monadic process execution functions necessary to idiomatically express the (safe) creation of a linux image. And, ideally, this stop-on-failure-monad could also become a stop-on-failure-and-rollback- monad. This way, my filesystem remains entirely safe despite performing a number of potentially dangerous commands as root.

Sources

module MaybeMonad = struct
type 'a t = None | Maybe of 'a
let return (a: 'a) : 'a t = Maybe a
let (>>=) (m: 'a t) (f: 'a -> 'b t) : 'b t = match m with
| None -> None
| Maybe a -> f a
let get (m: 'a t) (a: 'a) = match m with
| None -> a
| Maybe a -> a
end
let sum x_opt y_opt z_opt = let open MaybeMonad in
let sum_opt =
x_opt >>= fun a ->
y_opt >>= fun b ->
z_opt >>= fun c ->
return (a + b + c + 2) in
get sum_opt 0
let s = sum (MaybeMonad.Maybe 9) (MaybeMonad.Maybe 10) (MaybeMonad.Maybe 11)
let _ = assert (s = 32)
let s = sum (MaybeMonad.Maybe 9) (MaybeMonad.Maybe 10) MaybeMonad.None
let _ = assert (s = 0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment