Yoneda lemma
For a functor and an object , . In Haskell, is isomorphic to , and the left-hand form turns a chain of fmaps into one composed function and a single fmap.
The two directions: a natural transformation goes to , and an element goes to the transformation with components . Naturality of at , applied to , shows that is determined by that one element:
In Haskell every function of type is natural by parametricity, so the lemma becomes an isomorphism of types:
The Yoneda form of a functor, in Haskell.
newtype Yoneda f a = Yoneda (forall b. (a -> b) -> f b)liftYoneda :: Functor f => f a -> Yoneda f aliftYoneda fa = Yoneda (\k -> fmap k fa)lowerYoneda :: Yoneda f a -> f alowerYoneda (Yoneda y) = y idinstance Functor (Yoneda f) wherefmap g (Yoneda y) = Yoneda (\k -> y (k . g))
fmap on Yoneda f does not use f's fmap at all; it precomposes. A chain of fmaps becomes one composed function, and lowerYoneda applies it in a single traversal. That matters for any functor whose fmap walks a large structure.
The same, specialized to lists in OCaml, with a counter on traversals.
(* Yoneda for lists: a list, represented by what fmap would do to it. *)type 'a yoneda = { run : 'b. ('a -> 'b) -> 'b list }let traversals = ref 0let map f xs = incr traversals; List.map f xslet lift xs = { run = (fun k -> map k xs) }let lower y = y.run (fun x -> x)(* fmap on the Yoneda form does not touch the list: it composes. *)let fmap f y = { run = (fun k -> y.run (fun x -> k (f x))) }
Running it.
map h (map g (map f xs)) -> [2; 4; 6; 8; 10], traversals: 3lower (fmap h (fmap g (fmap f .))) -> [2; 4; 6; 8; 10], traversals: 1
The dual, Coyoneda, , makes any type constructor a functor: it stores the function until something that knows how to apply it comes along.
see also
- Higher-kinded typeA higher-kinded type is a type constructor, such as Maybe or List, that takes types as arguments, and higher-kinded polymorphism is the ability to abstract over such constructors with type variables, so that Functor f can be instantiated at Maybe or at lists without naming their element types. Kinds classify types the way types classify values: Int has kind Type, Maybe has kind Type → Type. Higher-kinded variables are what make a single Functor or Monad class usable across every container. Haskell and Scala support them directly; OCaml expresses the same abstraction with module functors or an encoding, and Java and Go cannot express it.
- HylomorphismA function that unfolds a seed into a recursive structure and folds that structure into a result, written so the structure is never built: hylo f g = f . fmap (hylo f g) . g. The call tree of the recursion is the intermediate structure.
- 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.
- LensA lens is a first-class pair of a getter and a setter for one part of a structure, with laws that make them agree: you get back what you set, setting what you got changes nothing, and a second set overwrites the first. Lenses compose, so a path through nested immutable records is one value that can read or update the part at its end. They originated in work on the view-update problem, and in Haskell are usually encoded as functions polymorphic in a functor, which makes composition ordinary function composition.
- 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.
- PrismA prism is the counterpart of a lens for sum types: a partial getter, preview, that succeeds only on one case, and a builder, review, that makes a whole value from that case's contents. Two laws make them agree. Prisms compose with each other, and a lens composed with a prism gives an affine traversal, which has at most one focus and no builder. They make deep edits into data like JSON or syntax trees safe when the path does not exist, and they are one of the standard optics, alongside lenses, isos and traversals.
- 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]S. Mac Lane, Categories for the Working Mathematician, Springer (2nd ed., 1998).
- [2]P. Wadler, “Theorems for free!”, FPCA (1989).
last updated