The next major release of the OCaml compiler, version 4.08, will be equipped
with a new syntax extension for
*monadic* and *applicative* composition. Practically it means that it will be
a bit more convenient to work with APIs structured around these
patterns. The design draws inspiration from
ppx_let but offers lighter syntax,
and removes the need of running the code through a ppx preprocessor.

Compared to similar extensions for languages like Haskell, F# and Scala, it’s interesting to note that the OCaml version is not only targeting monads but also supports a version for applicative functors.

This post contains some concrete examples of what the new syntax looks like and how to enable it.

As of writing, version 4.08 has not been officially released so in order to follow along you need to update your opam and switch to the beta release:

```
opam switch ocaml-variants.4.08.0+beta1
```

Alternatively, you can also use the latest version of `dune`

for building, which
has a backport of the syntax extension.

## An example - working with options

The examples below are about composing functions returning optional results using the monad and applicative functor combinators for options.

To have something concrete to work with, assume the following API:

```
val safe_head : 'a seq -> 'a option
val safe_tail : 'a seq -> 'a seq option
val safe_div : float -> float -> float option
```

### Monad syntax for options

The monadic composition combinators for the `option`

type may be defined
as a function `bind`

with the signature:

```
val bind : 'a option -> ('a -> 'b option) -> 'b option
```

Implemented as:

```
let bind o f =
match o with
| Some x -> f x
| None -> None
```

The convention is to also provide an infix version, as in:

```
let ( >>= ) o f = bind o f
```

It’s effectively used for composing sequences of functions yielding optional results.

As a silly example, consider writing a function that given a `float seq`

,
returns the value of the first two elements divided, if the sequence contains
at least two elements, and the division is successful. Using the API from
above and the monadic bind operator, it may be defined as:

```
let div_first_two xs =
safe_head xs >>= fun x ->
safe_tail xs >>= fun ys ->
safe_head ys >>= fun y ->
safe_div x y
```

We can write this differently using the syntax extension for monads, all that
is needed is another alias to `bind`

:

```
let (let*) x f = bind x f
```

That’s it, the same program can now be expressed as:

```
let div_first_two xs =
let* x = safe_head xs in
let* ys = safe_tail xs in
let* y = safe_head ys in
safe_div x y
```

In general, the expression:

```
let* x = e1 in e2 x
```

desugars to the equivalent of:

```
e1 >>= fun x -> e2 x
```

### Applicative syntax for options

Monads are great for composing dependent computations but not all
compositions are dependent. For the cases where monads are either an
overkill or just not feasible, applicative functors provide an alternative. You
can find some more information about this pattern in the context of OCaml,
here;
They are often described in terms of two functions `pure`

and
`apply`

, with the signatures:

```
val pure : 'a -> 'a t
val apply : ('a -> 'b) t -> 'a t -> 'b t
```

The infix version of `apply`

is usually called `(<*>)`

, i.e.:

```
let ( <*> ) fa xa = apply fa xa
```

How exactly do they supplement monads? By looking at the signature of
`apply`

, it is clear that in the expression, `f <*> x`

, both `f`

and `x`

are
applicative values that exist before the evaluation of `apply`

is performed.

In contrast, in the monadic composition expression, `x >>= f`

, the monad
value produced by `f`

is not known until it’s actually applied a value extracted
from `x`

.

We, therefore, say that applicatives provide *static* composition whereas
monads also support *dynamic* composition. In short, this makes monads more
powerful but less optimization friendly.

Below are the traditional applicative combinators defined for the `option`

type:

```
let pure x = Some x
let apply fo xo
match fo, xo with
| Some f, Some x -> Some (f x)
| _ -> None
let (<*>) fo xo = apply fo xo
```

And, here’s an example of how they’re used in defining a function that given
three `float seq`

values, adds their heads together, in case they’re all non-empty:

```
let add_heads xs yz zs =
pure (fun x y z -> x +. y +. z)
<*> safe_head xs
<*> safe_head ys
<*> safe_head zs
```

This function could of course also be written in a monadic style but what’s
nice about the applicative version is that it’s apparent from the definition that
there are no dependencies between the three calls to `safe_head`

.

If we were operating in some other applicative context, say `Async.t`

, we could in
fact run the extractions in parallel.

As for the OCaml syntax extension for applicatives, it’s actually not based on
`apply`

and `pure`

, but a pair of alternative combinators, often called `map`

and `product`

:

```
val map : ('a -> 'b) -> 'a t -> 'b t
val product : 'a t -> 'b t -> 'a * 'b t
```

To show that `apply`

and `product`

are equivalent, here’s how you define
the infix version of `apply`

in terms of `map`

and `product`

:

```
let ( <*> ) fa xa = map (fun (f, x) -> f x) @@ product fa xa
```

Going the other way around, one can also express `product`

using `pure`

and `apply`

:

```
let product xa ya = pure (fun x y -> (x, y)) <*> xa <*> ya
```

The implementations of `map`

and `product`

for the `option`

type are straight forward:

```
let map f = function
| None -> None
| Some x -> Some (f x)
let product o1 o2 =
match o1, o2 with
| Some x, Some y -> Some (x,y)
| _ -> None
```

Now, to enable the special syntax we just need to define `(let+)`

as `map`

with arguments
in reversed order and `(and+)`

as an alias for `product`

:

```
let (let+) x f = map f x
let (and+) o1 o2 = product o1 o2
```

Finally, using the syntax extension, we can rewrite the example above, like this:

```
let add_heads xs yz zs =
let+ x = safe_head xs
and+ y = safe_head ys
and+ z = safe_head zs in
x +. y +. z
```

Again, the syntax stresses how the three `safe_head`

computations are
independent. In fact, the compiler prevents us from introducing any
dependencies (just like with regular) `let .. and`

syntax).

In general, an expression, `let+ x = e1 and+ y = e2 in e3 x y`

, gets desugared to:

```
map (fun (x,y) -> e3 x y) (product e1 e2)
```

Or the equivalent expression, written in terms of `apply`

and `pure`

:

```
e3 <$> e1 <*> e2
```

Here, `(<$>)`

is the infix version of `map`

.

### Wrapping up

To conclude the examples for `option`

s, here’s a version where the operations
are wrapped in a module `Option`

with the syntax extensions contained in a
sub module. This allows users of the library to opt in for the new
syntax when needed (by opening `Option.Syntax`

):

```
module Option = struct
let map f = function
| None -> None
| Some x -> Some (f x)
let bind o f =
match o with
| Some x -> f x
| None -> None
let product o1 o2 =
match o1, o2 with
| Some x, Some y -> Some (x,y)
| _ -> None
module Syntax = struct
let (let+) x f = map f x
let (and+) o1 o2 = product o1 o2
let (let*) x f = bind x f
end
end
```

So, whenever you wish to extend your data type, `t`

, with
special syntax, implement a module like `Syntax`

, with the following
signature:

```
module Syntax : sig
(* Monad *)
val ( let* ) : 'a t -> ('a -> 'b t ) -> 'b t
(* Applicatives *)
val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
val ( and+ ) : 'a t -> 'b t-> ('a * 'b) t
end
```

If you’re only able to support the applicative subset, simply skip `( let* )`

.

## Final notes

As with any language extension, there is a tradeoff between the utility introduced and the additional complexity or cognitive load required in order to benefit from it. In this case I think it’s worth the price. In particular, it’s likely going to nudge developers toward the time-tested patterns of monads and applicative functors. Having more libraries with common looking APIs actually works in the opposite direction, reducing the amount of cognitive load required.