wiki

Applicative

A functor with pure : a -> f a and a way to combine independent computations, <*> : f (a -> b) -> f a -> f b, or equivalently product : f a -> f b -> f (a * b). Every monad is applicative, but applicatives are strictly more general: because later steps cannot depend on earlier results, effects can be accumulated, parallelised or inspected before running.

The laws, for and (written <*>):

An equivalent presentation replaces by a product, which makes the structure clearer: an applicative is a lax monoidal functor, with

associative and unital up to the isomorphisms and . The two are interdefinable: and .

Compared with a monad's , the product takes both computations up front. The second cannot depend on the first's result, and that restriction is what the next example relies on.

Validation. OCaml's let+ and and+ are exactly map and product, so an applicative gets its own syntax.

(* Validation: like result, but when both sides fail, both errors are kept.
That only works because the second computation does not depend on the
first one's result: it is applicative, not monadic. *)
type 'a validation = Ok of 'a | Errors of string list
let map f = function Ok x -> Ok (f x) | Errors e -> Errors e
let product a b =
match (a, b) with
| Ok x, Ok y -> Ok (x, y)
| Errors e, Ok _ | Ok _, Errors e -> Errors e
| Errors e1, Errors e2 -> Errors (e1 @ e2)
let ( let+ ) x f = map f x
let ( and+ ) = product
let nonempty field s = if s = "" then Errors [ field ^ " is empty" ] else Ok s
let age s =
match int_of_string_opt s with
| Some n when n >= 0 -> Ok n
| _ -> Errors [ "age is not a natural number" ]
type person = { name : string; email : string; age : int }
let person ~name ~email ~age:a =
let+ name = nonempty "name" name
and+ email = nonempty "email" email
and+ age = age a in
{ name; email; age }

Running it. All three errors are reported, not just the first.

Ok { name = "Ada"; email = "[email protected]"; age = 36 }
Errors ["name is empty"; "email is empty"; "age is not a natural number"]

A monad could not do this lawfully: with the second check is a function of the first result, which does not exist when the first check failed, so it has to stop there. The product has both sides and can keep both sides' errors.

The same restriction makes applicatives analysable. A parser built only from and has a fixed structure that can be inspected, optimised or printed as a grammar before any input is read, and a batch of independent requests can be sent at once instead of one after another.

see also

further reading

  1. [1]C. McBride, R. Paterson, “Applicative programming with effects”, Journal of Functional Programming 18 (2008).
  2. [2]The OCaml manual, “Binding operators”.