Maybe monad in ocaml

This is code i recovered from rosetta code. I do not fully understand it,

OCaml

The Option module already has our bind and return operations. If we wanted to redefine them :

let bind opt func = match opt with
  | Some x -> func x
  | None -> None
 
let return x = Some x

To easily manipulate bind, we can use infix operators and let pruning :

let (-->) = bind
let (let*) = bind
 
let print_str_opt x = 
  Format.printf "%s" (Option.value ~default:"None" x)

Example:

let safe_div x y = if y = 0.0 then None else return (x /. y)
let safe_root x = if x < 0.0 then None else return (sqrt x)
let safe_str x = return (Format.sprintf "%#f" x)
 
(* Version 1 : explicit calls *)
let () =
  let v = bind (bind (safe_div 5. 3.) safe_root) safe_str in
  print_str_opt v
 
(* Version 2 : with an operator *)
let () =
  let v = safe_div 5. 3. --> safe_root --> safe_str in
  print_str_opt v
 
(* Version 3 : let pruning really shine when inlining functions *)
let () =
  let v = 
    let* x = safe_div 5. 3. in
    let* y = if x < 0.0 then None else return (sqrt x) in
    return (Format.sprintf "%#f" y)
  in print_str_opt v

I’m not sure how to help you without knowing what exactly is unclear to you. Is it the option monad or the (let*)? The syntax of let operators is defined in the manual.

Since you called it “Maybe monad”, I assume you’re looking at OCaml’s parallel to Haskell’s. So let’s go at this step-by-step…


first of all the data type:

data Maybe a = Nothing | Just a

is the same as OCaml’s option, already defined in stdlib

type 'a option = None | Some of 'a

second of all, the monad interface:

class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  return :: a -> m a

which can be defined as an OCaml module-type

module type Monad = sig
  type 'a m
  val (>>=) : 'a m -> ('a -> 'b m) -> 'b m
  val return : 'a -> 'a m
end

third of all, the implementation (there are sugars here that I skipped):

instance Monad Maybe where
  (>>=) m f = case m of
    Nothing -> Nothing
    Just x -> f x
  return x = Just x

which we can translate to OCaml

module Option_monad : Monad = struct
  type 'a m = 'a option
  let (>>=) m f = match m with
    | Some x -> f x
    | None -> None
  let return x = Some x
end

OCaml’s Option module has return under a different name: some, and (>>=) under a function name not an operator: bind.


fourth of all, notation… In Haskell, your sugar is the do-notation:

nine :: Maybe Int
nine = do
  x <- return 4
  return (5 + x)

while in OCaml, your sugar is the let-operators

module Option_monad_sugar = struct
  let (let*) = Option_monad.(>>=) (* or Option.bind *)
  let return = Option_monad.return (* or Option.some *)
end
let nine = Option_monad_sugar.(
  let* x = return 4 in
  return (5 + x)
)

notice that what a “monad” is from a programming perspective is just an interface with implied discipline (Haskell does not prove that every instance follows that discipline, but you should)…
That discipline is that every instance of Monad should follow three rules (in pseudo-ocaml, with ≡ meaning equivalence):

forall m, f, g, x:
(* 1 *) m >>= return    ≡ m
(* 2 *) return x >>= f  ≡ f x
(* 3 *) (m >>= f) >>= g ≡ m >>= (fun x -> f x >>= g)

if your implementation of the Monad interface holds all these rules true, it’s considered “monadic”.
Try to write assertions in actual OCaml with the Option_monad implementation to see if it’s monadic.


fifth of all, what all this means…

You’re free to use OCaml’s option or Haskell’s Maybe without paying any attention to the Monad interface, and still write safe software that can express a “null” value. What the interface offers you is what any other interface offers you: a way to write functions that do not care about the actual implementation being Option, or MyCoolModule. What giving it a mathematical name like “Monad” does is just attach implications from such field (the three rules) which you can use to your advantage or ignore.

12 Likes