Skip to content

Instantly share code, notes, and snippets.

@vietlq
Forked from eatonphil/monad.ml
Last active June 11, 2018 15:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vietlq/3d93141865d9620032b44f5a2aadc4fa to your computer and use it in GitHub Desktop.
Save vietlq/3d93141865d9620032b44f5a2aadc4fa 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)
(* https://andrewray.github.io/iocamljs/full402.html *)
module MaybeMonad = struct
let return (a : 'a) : 'a option = Some a
let (>>=) (m : 'a option) (f : 'a -> 'b option) : 'b option =
match m with
| None -> None
| Some a -> f a
end
open MaybeMonad
let data1 = Some "api1"
let data2 = Some "api2"
let data3 = Some "api3"
let result =
data1 >>= (fun x ->
data2 >>= (fun y ->
data3 >>= (fun z ->
return (x ^ ":" ^ y ^ ":" ^ z) )))
let result2 =
data1 >>= fun x ->
data2 >>= fun y ->
data3 >>= fun z ->
return (x ^ ":" ^ y ^ ":" ^ z)
let _ = match result with
| None -> "NOTHING"
| Some x -> x
let _ = match result2 with
| None -> "NOTHING"
| Some x -> x
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment