Applicative
An applicative functor is a functor with two further operations, and (written <*>), that combine independent computations, satisfying four laws. It sits between functors and monads: every monad is applicative, but not every applicative is a monad. Because each step's computation is fixed before any of them runs, an applicative computation can collect every error, run its parts in parallel, or be inspected before it is run, none of which a monadic one can.
Overview
A functor maps a function of one argument over a structure. Mapping a function of two arguments over two structures needs more: fmap f a gives a structure of functions, and something has to apply those to the values in the second structure. That is :
and the same pattern lifts a function of any number of arguments. What "apply" means depends on the functor. For option the result exists only if both arguments do. For lists every function meets every argument, giving all combinations. For functions both are applied to the same .
pure and ap for option, lists (all combinations), zipped lists (pointwise), and functions.
(* pure and ap for option and list; map2 and product follow. *)module Option_a = structlet pure x = Some xlet ap mf mx = match (mf, mx) with Some f, Some x -> Some (f x) | _ -> Nonelet map2 f a b = ap (ap (pure f) a) bendmodule List_a = structlet pure x = [ x ](* every function applied to every argument *)let ap fs xs = List.concat_map (fun f -> List.map f xs) fslet map2 f a b = ap (ap (pure f) a) bend(* The zip applicative: same functor as lists, different combination. *)module Zip_a = structlet ap fs xs = List.map2 (fun f x -> f x) (List.filteri (fun i _ -> i < List.length xs) fs)(List.filteri (fun i _ -> i < List.length fs) xs)let map2 f a b = ap (List.map f a) bend(* Functions out of a fixed type: pure is K and ap is S. *)module Fun_a = structlet pure x = fun _ -> xlet ap f g = fun r -> f r (g r)end
Running it.
Option_a.map2 ( + ) (Some 1) (Some 2) -> Some 3Option_a.map2 ( + ) (Some 1) None -> NoneList_a.map2 ( + ) [1; 2] [10; 20] -> [11; 21; 12; 22]Zip_a.map2 ( + ) [1; 2] [10; 20] -> [11; 22]Fun_a: (fun s n -> s / n) <$> sum <*> length applied to [2; 4; 9] -> 5
Lists have two lawful applicative instances with the same map: the cartesian one and the zipping one. The zipping one's pure has to be an infinite list, so that it can be zipped with a list of any length, which is why Haskell's ZipList needs laziness and why the OCaml version above leaves it out. For functions, pure is the K combinator and is S: and , exactly the SKI combinators.
Definition
An applicative functor is a functor with
Laws
Together they say that pure adds no effect and that the effects of a combination happen in the order the arguments are written, independently of the functions being applied. A consequence is that any expression built from pure and can be put in the canonical form : the pure function first, then the effects in order[1]. The functor's map is .
The four laws for the list applicative on random lists and functions.
(* The four applicative laws for the list applicative, on random inputs. *)let pure x = [ x ]let ap fs xs = List.concat_map (fun f -> List.map f xs) fslet compose f g x = f (g x)let () = Random.init 11let rand_list () = List.init (Random.int 4) (fun _ -> Random.int 20)let rand_fns () = List.init (Random.int 3) (fun _ -> let k = Random.int 5 in fun x -> (x * 3) + k)let identity () = let v = rand_list () in ap (pure Fun.id) v = vlet composition () =let u = rand_fns () and v = rand_fns () and w = rand_list () inap (ap (ap (pure compose) u) v) w = ap u (ap v w)let homomorphism () = let x = Random.int 20 and f = fun x -> x * x in ap (pure f) (pure x) = pure (f x)let interchange () = let u = rand_fns () and y = Random.int 20 in ap u (pure y) = ap (pure (fun f -> f y)) u
Running it.
identity holds on 10000 random casescomposition holds on 10000 random caseshomomorphism holds on 10000 random casesinterchange holds on 10000 random cases
Monoidal form
An equivalent definition replaces by a product:
associative and unital up to the isomorphisms and . The two are interdefinable:
This form is often easier to implement and makes the structure plain: an applicative is a way of pairing up two structures of the same shape. It is also the form OCaml's syntax uses.
Notation
Haskell writes f <$> a <*> b <*> c, with <$> for fmap; McBride and Paterson called such expressions idioms and proposed brackets for them[1]. Haskell's ApplicativeDo extension lets do-notation desugar to applicative operations when no step uses an earlier result.
OCaml's binding operators have a form made for applicatives. With let+ defined as map and and+ as the product,
Every bound expression is evaluated independently of the others, and the body sees all their results[8].
Examples
Validation
The standard example of an applicative that is not a monad. A validation either succeeds or holds a list of errors, and the product of two failures keeps both lists:
Validating an order with let+ and and+, and the same checks with Result.bind.
type 'a validation = Ok of 'a | Errors of string listlet map f = function Ok x -> Ok (f x) | Errors e -> Errors elet 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)(* OCaml's binding operators for applicatives: let+ is map, and+ is product. *)let ( let+ ) x f = map f xlet ( and+ ) = productlet nonempty field s = if s = "" then Errors [ field ^ " is empty" ] else Ok slet natural field s =match int_of_string_opt s with Some n when n >= 0 -> Ok n | _ -> Errors [ field ^ " is not a natural number" ]type order = { item : string; quantity : int; customer : string }let order ~item ~quantity ~customer =let+ item = nonempty "item" itemand+ quantity = natural "quantity" quantityand+ customer = nonempty "customer" customer in{ item; quantity; customer }(* The same checks with Result.bind stop at the first failure. *)let order_monadic ~item ~quantity ~customer =let ( let* ) = Result.bind inlet check = function Ok x -> Stdlib.Ok x | Errors e -> Stdlib.Error e inlet* item = check (nonempty "item" item) inlet* quantity = check (natural "quantity" quantity) inlet* customer = check (nonempty "customer" customer) inStdlib.Ok { item; quantity; customer }
Running it. The applicative version reports every problem; the monadic one stops at the first.
applicative -> item is empty; quantity is not a natural number; customer is emptymonadic -> item is empty
No lawful monad can behave like the applicative here. With the check on quantity is a function of the result of the check on item, and when that check failed there is no result to call the function with. The product receives both checks up front, and can keep both failures.
Parsers
Parser combinators built from pure and alone describe a context-free grammar whose structure is known before any input is read, which parsers can use to compute lookahead sets and recover from errors[4]. A monadic parser can choose its next parser from a value it has parsed, which is more expressive and cannot be analysed ahead of time. See parser combinators.
Static structure
Because an applicative computation's shape does not depend on any result, it can be examined without running it. A form built from fields knows which fields it needs:
A form description whose list of fields is computed without any input.
(* A form description that knows its fields before it is run: possiblebecause an applicative's structure does not depend on parsed values. *)type 'a form = { fields : string list; parse : (string * string) list -> 'a option }let field name = { fields = [ name ]; parse = (fun input -> List.assoc_opt name input) }let pure x = { fields = []; parse = (fun _ -> Some x) }let map f a = { a with parse = (fun i -> Option.map f (a.parse i)) }let product a b ={ fields = a.fields @ b.fields;parse = (fun i -> match (a.parse i, b.parse i) with Some x, Some y -> Some (x, y) | _ -> None) }let ( let+ ) x f = map f xlet ( and+ ) = productlet signup =let+ user = field "user"and+ email = field "email"and+ plan = field "plan" inPrintf.sprintf "%s <%s> on %s" user email plan
Running it.
signup.fields -> [user; email; plan] (known without any input)signup.parse -> ada <[email protected]> on pro
Larger systems use the same property. Facebook's Haxl data-fetching library batches and deduplicates the requests in the independent parts of an applicative expression[2]. In build systems, applicative tasks have dependencies known before any task runs, while monadic tasks can discover them as they go[3].
Traversals
A traversable structure is one whose elements can be visited in order while an applicative effect is accumulated:
Only the applicative operations are needed, which is why traverse works for validations and zipped lists as well as for monads. For option it fails if any element fails; for lists, sequence of a list of lists is their cartesian product.
traverse for lists, written once for any applicative passed to the functor.
(* traverse for lists, written once for any applicative given as a module. *)module type APPLICATIVE = sigtype 'a tval pure : 'a -> 'a tval map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c tendmodule Traverse (A : APPLICATIVE) = structlet traverse f xs = List.fold_right (fun x acc -> A.map2 List.cons (f x) acc) xs (A.pure [])let sequence xs = traverse Fun.id xsendmodule T_option = Traverse (structtype 'a t = 'a optionlet pure x = Some xlet map2 f a b = match (a, b) with Some x, Some y -> Some (f x y) | _ -> Noneend)module T_list = Traverse (structtype 'a t = 'a listlet pure x = [ x ]let map2 f a b = List.concat_map (fun x -> List.map (f x) b) aend)
Running it.
T_option.traverse int_of_string_opt ["4"; "8"] -> Some [4; 8]T_option.traverse int_of_string_opt ["4"; "x"] -> NoneT_list.sequence [[1; 2]; [3]; [4; 5]] -> [[1; 3; 4]; [1; 3; 5]; [2; 3; 4]; [2; 3; 5]]
Traversals satisfy laws of their own, which say that visiting every element exactly once is the only thing traverse can do[5].
Relation to functors and monads
Each abstraction adds one capability. A functor maps over one structure. An applicative combines several structures whose effects are all fixed in advance. A monad also lets the next computation depend on the result of the previous one. Every monad gives an applicative:
but validation and zipped lists are applicatives with no compatible monad. The extra power of is paid for by losing everything that depends on knowing the structure in advance: static analysis, batching, parallel execution and error accumulation. Lindley, Wadler and Yallop compared the three precisely: idioms are "oblivious" to earlier results, arrows "meticulous" and monads "promiscuous"[6].
Category theory
In category theory an applicative functor is a lax monoidal functor with a strength, on a category with finite products. Lax monoidal means there are maps
natural in and and compatible with the associativity and unit isomorphisms of the product[7]. These are the unit and product of the monoidal form above. The strength, which lets a value from outside the functor be paired with one inside it, is automatic for functors on the category of types and functions, which is why the programming definition does not mention it.
History
McBride and Paterson introduced applicative functors in a paper circulated from 2005 and published in 2008, under the name idioms[1]; the pattern had appeared earlier in parser combinator libraries[4]. Haskell's standard library made Applicative a superclass of Monad in 2015, so every monad must now define the applicative operations as well.
see also
- FunctorIn functional programming, a functor is a type constructor f with an operation fmap : (a -> b) -> f a -> f b that applies a function inside the structure without changing its shape, preserving identity and composition. Lists, options, trees and functions out of a fixed type are functors. The notion comes from category theory. In OCaml the word also names parametrised modules such as Map.Make.
- MonadIn functional programming, a monad is a type constructor m with two operations, return : a -> m a and bind : m a -> (a -> m b) -> m b, satisfying three laws. It lets code with some extra behaviour, such as failure, several results, configuration, state or I/O, be written as a sequence of ordinary steps, with the behaviour defined once in bind. The notion comes from category theory.
- Parser combinatorA parser combinator is a higher-order function that builds a parser from smaller parsers: a sequence of two parsers, a choice between them, a repetition of one. Parsers are ordinary values in the host language, so a grammar is written as a program that is itself the parser, with no separate generator. Parser combinators produce recursive descent parsers; they cannot use left-recursive rules directly, and with unlimited backtracking can take exponential time, which memoization (packrat parsing) reduces to linear.
- SKI combinatorsThree combinators, S x y z = x z (y z), K x y = x and I x = x, from which every closed lambda term can be built by application alone, with no variables. I is redundant, since S K K x = x. The translation from lambda terms is called bracket abstraction.
- TraversableTraversable is the abstraction of a container that can be rebuilt while an applicative effect is performed on each element in order: traverse : (a -> f b) -> t a -> f (t b). Validating every element, parsing each string, numbering the nodes of a tree and taking cartesian products are all traversals with different applicative functors. traverse subsumes both map (with the identity functor) and foldMap (with a constant functor into a monoid), and it satisfies laws saying it visits every element exactly once.
- MonoidA monoid is a set with an associative binary operation and an identity element for it: integers under addition with 0, strings under concatenation with the empty string, functions under composition with the identity. Associativity means a sequence can be combined with any bracketing, so a fold over a monoid can be split into parts and computed in parallel or incrementally. Lists are the free monoid, and every monoid-valued function on elements extends uniquely to lists.
- Kleisli compositionKleisli composition composes two functions that return monadic results, f : a → m b and g : b → m c, into a function a → m c, by applying f and binding its result to g. It is written >=> in Haskell. Functions of this shape are the arrows of the Kleisli category of the monad, with return as the identity, and the monad laws are exactly the statement that this composition is associative and has return as a unit on both sides.
- Van Laarhoven lensA van Laarhoven lens is a lens represented as a single function polymorphic over functors: forall f. Functor f => (a -> f a) -> s -> f s. Instantiating the functor gives the operations: the constant functor yields the getter and the identity functor the setter. Lenses in this form compose with ordinary function composition, and replacing Functor with Applicative gives traversals, which compose with lenses the same way. The representation is the basis of Haskell's lens library.
further reading
- [1]C. McBride, R. Paterson, “Applicative programming with effects”, Journal of Functional Programming 18 (2008).
- [2]S. Marlow, L. Brandy, J. Coens, J. Purdy, “There is no fork: an abstraction for efficient, concurrent, and concise data access”, ICFP (2014).
- [3]A. Mokhov, N. Mitchell, S. Peyton Jones, “Build systems à la carte”, ICFP (2018).
- [4]S. D. Swierstra, L. Duponcheel, “Deterministic, error-correcting combinator parsers”, Advanced Functional Programming, LNCS 1129 (1996).
- [5]M. Jaskelioff, O. Rypacek, “An investigation of the laws of traversals”, MSFP (2012).
- [6]S. Lindley, P. Wadler, J. Yallop, “Idioms are oblivious, arrows are meticulous, monads are promiscuous”, MSFP (2008).
- [7]S. Mac Lane, Categories for the Working Mathematician, Springer (2nd ed., 1998).
- [8]The OCaml manual, “Binding operators”.
last updated