Kleisli composition
A function for a monad is a computation with an effect: it may fail, return several results, or update state. Such functions do not compose with ordinary function composition, since the output type is not the input type of the next function. Kleisli composition fills the gap[3][5].
Definition
Kleisli composition applies and passes its result to with bind:
It is the composition of the Kleisli category of the monad, whose objects are types and whose arrows from to are functions [1][4].
The monad laws
Stated with >=>, the three monad laws say that return is a left and right identity and that composition is associative[3]:
These are the category laws, which is why they are easier to remember in this form than in terms of bind. A monad is lawful exactly when its Kleisli arrows form a category.
Example
Kleisli composition for any monad, a pipeline of partial steps in the option monad, a nondeterministic pipeline in the list monad, and a randomized check of the laws.
(* Kleisli composition for a monad given by return and bind. *)module type MONAD = sigtype 'a tval return : 'a -> 'a tval bind : 'a t -> ('a -> 'b t) -> 'b tendmodule Kleisli (M : MONAD) = structlet ( >=> ) f g x = M.bind (f x) gendmodule Opt = Kleisli (struct type 'a t = 'a option let return x = Some x let bind = Option.bind end)module Lst = Kleisli (struct type 'a t = 'a list let return x = [ x ] let bind l f = List.concat_map f l end)(* Partial steps compose into a partial pipeline. *)let parse s = int_of_string_opt (String.trim s)let positive n = if n > 0 then Some n else Nonelet half n = if n mod 2 = 0 then Some (n / 2) else Nonelet pipeline = Opt.(parse >=> positive >=> half)(* Nondeterministic steps: a knight's moves on an 8x8 board. *)let moves (c, r) =List.filter (fun (c, r) -> c >= 1 && c <= 8 && r >= 1 && r <= 8)(List.map (fun (dc, dr) -> (c + dc, r + dr))[ (1, 2); (2, 1); (2, -1); (1, -2); (-1, -2); (-2, -1); (-2, 1); (-1, 2) ])let in_three = Lst.(moves >=> moves >=> moves)
Running it.
" 42" -> 21"-8" -> None"7" -> None"x" -> Noneknight from a1: 64 paths of three moves, 22 squares, h8 reachable: falseidentity and associativity hold in 10000 of 10000 random cases
In the option monad a pipeline stops at the first step that fails: "-8" parses but is not positive, and "7" is positive but odd. In the list monad each step returns all possibilities, so composing moves three times enumerates every sequence of three knight moves; h8 cannot be reached in three moves from a1, since a knight changes square colour on every move and the two corners have the same colour.
History
Heinrich Kleisli constructed the category now named after him in 1965, showing that every monad arises from an adjunction[1]. Moggi used monads and their Kleisli categories to model computational effects in 1989 and 1991[2], and Wadler brought monads into functional programming[3]. Haskell's Control.Monad provides >=> and its flipped form <=<[5].
see also
- 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.
- ApplicativeAn applicative functor is a functor with pure : a -> f a and an operation that combines independent computations, <*> : f (a -> b) -> f a -> f b, or equivalently a product f a -> f b -> f (a * b), satisfying four laws. It sits between functors and monads: every monad is applicative, but because later steps cannot depend on earlier results, an applicative computation can collect all errors, run its parts in parallel, or be inspected before it runs.
- 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.
further reading
- [1]H. Kleisli, “Every standard construction is induced by a pair of adjoint functors”, Proceedings of the AMS 16 (1965).
- [2]E. Moggi, “Notions of computation and monads”, Information and Computation 93 (1991).
- [3]P. Wadler, “Monads for functional programming”, Advanced Functional Programming, LNCS 925 (1995).
- [4]S. Mac Lane, Categories for the Working Mathematician, ch. VI, Springer (1971).
- [5]The Haskell base library documentation, Control.Monad, (>=>) and (<=<).
last updated