Applicatives in F#
Tags: fsharp, dotnet, functional
Published: 2021-05-13 17:58:31

# Background

Previously, we learned about [applicative functors](https://dev.to/shimmer/beautiful-folds-in-f-part-3-applicatives-4d1b), which are souped-up functors that support multi-argument mapping functions. In this post, I'd like to dive deeper into the practical usage of applicatives in F#.

We described an applicative as a type that:
* Is a [functor](https://dev.to/shimmer/functors-in-f-2fd7), which means that it has a unary (i.e. single-argument) `map` function (and, thus, an infix version of the same function, called `<!>`).
* Provides a way to apply arguments one at a time, via a `<*>` function.
* Provides a way to "`lift`" values into the applicative.

# Example

F#'s `Option<'t>` type is an applicative:

```fsharp
let (<!>) = Option.map

let (<*>) fOpt xOpt =
    match fOpt, xOpt with
        | Some f, Some x -> Some (f x)
        | _ -> None

let lift x = Some x
```

This allows us, for example, to use multiplication to "map" over two options at once:

```fsharp
let mult = (*)

// functor: times3 takes one argument
let times3 = mult 3
Option.map times3 (Some 4)   // Some 12
Option.map times3 None       // None

// applicative: mult takes two arguments
mult <!> Some 3 <*> Some 4   // Some 12
mult <!> None   <*> Some 4   // None
mult <!> Some 3 <*> None     // None
```

# Pipes

Applying arguments one at a time with `<*>` is certainly a legitimate way to think about applicatives, but (IMHO) it's not the most intuitive, nor is it used often in F#. I think it's easier to conceptualize applicatives with the lifted function (e.g. `mult`) at the end of a computation, instead of the beginning. Following the FParsec library, we'll call this "[pipes](https://www.quanttec.com/fparsec/reference/primitives.html#members.pipe2)" style:

```fsharp
let pipe2 a b f = f <!> a <*> b
```

In the case of options, `pipe2` has the following signature:[^1]

```fsharp
Option<'a> -> Option<'b> -> ('a -> 'b -> 'c) -> Option<'c>
```

So `pipe2` is a function that sends the result of computations `a` and `b` to function `f`, producing a new computation. We can see it in action with options here:

```fsharp
pipe2 (Some 3) (Some 4) mult   // Some 12
pipe2 None (Some 4) mult       // None
```

We can define `pipe3`, `pipe4`, etc. similarly.

Using pipes style, we can think of applicatives as performing computations in parallel at the same time, with no dependencies between them. For example, consider:

```fsharp
let runMult getOptA getOptB =
    pipe2
        (getOptA ())
        (getOptB ())
        mult
```

Note that `getOptB` is executed even if `getOptA` returns `None`. There's no way to short-circuit the computation before `getOptB` is invoked.

# Monad-lite

Contrast this with monads, which are a way of performing computations in series, so that the result of one computation flows into the next. In fact, instead of thinking of applicatives as souped-up functors, it's often easier to think of them as a "lite" version of monads.[^2] Let's see where this notion takes us, starting with a basic computation builder for options:

```fsharp
type OptionBuilder() =
    member __.Return(x) = Some x
    member __.Bind(opt, binder) = Option.bind binder opt

let option = OptionBuilder()
```

Using this builder, we can create monadic computation expressions, such as:

```fsharp
option {
    let! a = optA
    let! b = optB
    return a * b
}
```

Note that the computation of `b` does not depend in any way on the value of `a`, so a monad is overkill for this computation - it is merely applicative. We can enforce this limitation by removing the `Bind`  method from the computation builder, but then the expression doesn't compile at all.

# F# 5 enhancements

Fortunately, F# now supports applicative computation expressions directly. There are two new core builder methods:

* `BindReturn`: Corresponds to `<!>`, which is just `map`.
* `MergeSources`: Similar to `<*>` and `pipe2`, except that it always produces a 2-tuple (instead of calling a function).

For our `option` builder, their implementation looks like this:

```fsharp
member __.BindReturn(opt, f) =
    Option.map f opt

member __.MergeSources(optA, optB) =
    match optA, optB with
        | Some a, Some b -> Some (a, b)
        | _ -> None
```

Now that these two methods are present in the builder, `and!` can be used instead of `let!` to create an applicative expression:

```fsharp
option {
    let! a = optA
    and! b = optB   // OK because b does not depend on a
    return a * b
}
```

Because the applicative computations are independent, they can potentially be parallelized by the compiler, resulting in more efficient code. So be on the lookout for opportunities to use `and!` instead of `let!` in your F# code!

# References

* [F# applicatives RFC](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1063-support-letbang-andbang-for-applicative-functors.md)
* [F# 5 applicatives announcement](https://devblogs.microsoft.com/dotnet/announcing-f-5-preview-1/#applicative-computation-expressions)

[^1]: Haskell calls this function `liftA2` instead of `pipe2`, where the `A` stands for "applicative".
[^2]: So, just as every applicative is also a functor, every monad is also an applicative. However, not every applicative is a monad,  which is what makes the applicative pattern useful on its own, situated between its two better-known cousins.