Do Notation in OCaml
Asked Answered
A

2

7

Does OCaml have an equivalent to Haskell's Do Notation?

Another way to put it - is there an easy way to handle nesting monadic operations more easily... cause this is annoying:

open Batteries
open BatResult.Infix

let () =
  let out = 
    (Ok("one")) >>=
      (fun a -> 
        let b = Ok("two") in
        b >>= (fun c -> 
          print_endline(a);
          print_endline(c);
          Ok(c)
          )) in
  ignore(out)
  
Affectation answered 26/5, 2021 at 6:20 Comment(1)
It is much less annoying when you remove excessive parentheses around lambdas and indent them equally, as all OCaml auto-indentation tools do. In that form it often more readable than the do notation or let-operators.Ignacia
A
10

Yes, since OCaml 4.08, it is possible to describe let operators. For example:

let (let*) x f = BatResult.bind x f 
let (let+) x f = BatResult.map f x

This makes it possible to write programs in a style close to the direct style:

let out = 
  let* a = Ok "one" in 
  let* b = Ok "two" in 
  let () = print_endline a in 
  let () = print_endline b in 
  Ok b

You can describe a large number of operators, for monads (e.g. let* for bind and let+ for map), for applicative (e.g. let+ for map and and+ for product (or zip)) etc.

Otherwise it is possible to use syntax extensions, for example https://github.com/janestreet/ppx_let which, for the moment, offers even more possibilities than the let operators. If you ever want examples of let operators, in Preface (shameless plug), we define plenty of them!

edit: As @ivg says, you can use any of $ ∣  & ∣  * ∣  + ∣  - ∣  / ∣  = ∣  > ∣  @ ∣  ^ ∣  | for defining your let or and operator.

see: https://ocaml.org/manual/bindingops.html

Asta answered 26/5, 2021 at 7:42 Comment(2)
It also worth mentioning that you're not limited to * and +, but can use (nearly) any operator symbol and more than one if them.Ignacia
I add just a small edit adding the references and the allowed characters. Thanks!Asta
I
7

Unless you specifically try to obfuscate your code, writing monadic code without do-notation is pretty easy, e.g., if we will remove excessive1 parentheses from your example it will be already pretty readable,

let v0 =
  Ok "one" >>= fun a ->
  Ok "two" >>= fun c ->
  print_endline a;
  print_endline c;
  Ok c

And if we will use let-binding operators, e.g., using the monads [1,2] library (also a shameless plug), then we can write it even more concise,

open Monads.Std

open Monad.Result.Error.Syntax
open Monad.Result.Error.Let

let v1 =
  let+ a = Ok "one" and+ b = Ok "two" in
  print_endline a;
  print_endline b;
  b

1) OCaml denotes the application operation (both an application of a function to its argument and an application a constructor to its argument) by juxtaposition, e.g., sin x (not sin(x)), or Ok 42 (not Ok (42)), moreover the application operator has higher precedence (binds tighter) than infix operators, so you can write sin x > cos x. The lambda operator, fun <var> -> <expr> chains nicely with monadic operators, e.g.,

x >>= (fun x -> y >>= (fun y -> x + y))

is the same as

x >>= fun x -> y >>= fun y -> x + y

or, more often it is written as

x >>= fun x -> 
y >>= fun y ->
x + y

as a tribute to the let-legacy of the lambda (abstraction) operator, cf.,

let* x = x in
let* y = y in
x + y

and latest versions of OCaml even allow you to pun the right-hand side, e.g.,

let* x in
let* y in
x + y

or even

let* x and* y in
x + y
Ignacia answered 26/5, 2021 at 13:14 Comment(2)
This is an awesome answer, thank you. Removing the paraens definitely makes it easier to read. I'm going to mark X. Van de Woestyne's answer correct though because it answers the do notation more concisely though, and I couldn't find that anywhere else.Affectation
Sure, I was just adding a little bit more context to the answer, but Xavier has provided a perfect answer to your question, so it should be the accepted answer :)Ignacia

© 2022 - 2024 — McMap. All rights reserved.