The Typeclassopedia - A summary
This is a summary of Brent Yorgey's excellent article "The Typeclassopedia", originally published in issue #13 of "The Monad Reader".
Table of Contents
1 Functor
- "container" with ability to apply a function to every element
fmap
applies function to each element of the container without altering the structure of the container
- a functor represents some sort of "computational context"
fmap
applies function without altering context
- example: a list and the
map
function
- exported by the
Prelude
- instances
[]
Maybe
Either e
IO
Tree
Map
Sequence
Stream
Set
is NOT a functor in Haskell because of theOrd
type constraint
- laws
fmap id = id
mapping the identity has no effect
fmap (g . h) = fmap g . fmap h
mapping a function composition has the same effect as first mapping one and then the other function
fmap :: Functor f => (a -> b) -> f a -> f b
fmap :: (a -> b) -> (f a -> f b)
the extra parenthesis show thatfmap
transforms a "normal" function into one operating on containers/contexts => "lift"
2 Pointed
- not in the standard libraries, but in
category-extras
- represents pointed functors
- useful for understanding
Applicative
andMonad
- Pointed represents additional ability for putting value in default context
- defines a
pure
function aka singleton, return, unit, point
for exampleJust
forMaybe
- most standard
Functor
instances could be instances ofPointed
- law
fmap g . pure = pure . g
- impossible to write an instance that does not satisfy it
3 Applicative
- defined in
Control.Applicative
- newer addition to standard Haskell type classes
- applicative functors are exactly in between
Functor
andMonad
- every
Applicative
instance must also be aFunctor
- adds one capability to
Pointed
, applying a function which itself is in a context
<*>
is basically just function application within a context
- laws
fmap g x = pure g <*> x
g <$> x = pure g <*> x
mapping a function
g
over a contextx
is the same as injectingg
into the context withpure
and then applying it tox
with<*>
- other laws relate to
pure
- instances
- most instances of
Functor
Const
type constructor (defined inApplicative
)
WrappedMonad
andWrappedArrow
newtypes
- most instances of
4 Monad
- special because Haskell made them framework for I/O operations
- special because of syntactic sugar (do-notation)
- older than
Applicative
andArrow
- exported by the
Prelude
, many utility functions inControl.Monad
- additional instances in
Control.Monad.Instances
- methods
return
is the same aspure
(>>)
comes with a default implementation
fail
is a hack with no place in theMonad
class
(>>=)
akabind
makesMonad
more powerful thanApplicative
(>>=)
combines 2 computations into 1 larger computation
- structure of a monadic computation can change depending on intermediate values; more powerful than
Applicative
where structure is fixed
- instances
Identity
Maybe
models computations which may fail
[]
similar toApplicative
, models non-deterministic computations
IO
implemented in compiler specific ways; allows to build up values representing possibly effectful computations
((->) e)
aka "reader monad" for computations with a read-only environmente
(seeControl.Monad.Reader
)
Writer
(defined inControl.Monad.Writer
) collects information as computation progresses
State
(Control.Monad.State
) representes stateful computations
Cont
(Control.Monad.Cont
) models computations in CPS; has been called the "mother of all monads"
- utility functions
join
removes one level of monadic structure
liftM
is likefmap
ap
is equivalent to<*>
sequence
combines list of computations into one which collects a list of their results
replicateM
is a combination ofreplicate
andsequence
mapM
maps first argument over second and sequences results
(=<<)
is(>>=)
with reversed arguments
(>=>)
is like function composition but with an extram
on the result type of the functions and swapped arguments
- many of the above function have underscore variants (like
mapM_
that are only used for side-effects and throw away the results of the computations)
- laws
return a >>= k = k a
(injecting and binding is the same as applying)
m >>= return = m
(binding and returning leavesm
unchanged)
m >>= (\x -> kx >>= h) = (m >>= k) >>= h
((>>=)
is sort of associative)
fmap f xs = xs >>= return . f = liftM f xs
(ensures thatfmap
andliftM
are the same for types which are instances ofFunctor
andMonad
)
- alternative laws using
(>=>)
return >=> g = g
(return
is the identity of(>=>)
)
g >=> return = g
(same as above)
(g >=> h) >=> k = g >=> (h >=> k)
((>=>)
is associative)
- do-notation
- syntatactic sugar for "imperative style" programming
- do-blocks are recursively translated into monad operations
- plays strongly on "computational context" interpretation rather than "container" interpretation
- syntatactic sugar for "imperative style" programming
- monad transformers
- used for combining monads into a new monad
State
,ReaderT
,ErrorT
and (soon)MaybeT
- build composite monads "inside out" (see lambdabot's
@unmtl
command)
- all monad transformers implement the
MonadTrans
typeclass (Control.Monad.Trans
)
- used for combining monads into a new monad
MonadFix
- describes monads which support the fixpoint operation
mfix
mfix :: (a -> m a) -> m a
: allows output of monadic computation to be defined via recursion
mdo
is a "recursive do" notation
- describes monads which support the fixpoint operation
5 Monoid
- a set
S
with an associative binary operation⊕
and an identity element in respect to that operation
- examples:
- natural numbers under addition
- integers under multiplication
- boolean values under conjuction and disjunction
- natural numbers under addition
- defines several functions
mempty
is the identity element
mappend
is the binary operation
mconcat
's default implementations foldsmappend
, usually sufficient
- laws
mempty `mappend` x = x
x `mappend` mempty = x
(x `mappend` y) `mappend` z = x `mappend` (y `mappend` z)
- instances
- defined in
Data.Monoid
[a]
, withmempty = []
andmappend = (++)
Sum
andProduct
newtype wrappers for numeric types under addition/multiplication
Any
andAll
as newtype wrappers forBool
under disjunction and conjunction
Maybe
, as well asFirst
andLast
Endo a
, newtype wrapper foa -> a
, which form a monoid under composition
Ordering = LT | EQ | GT
withmempty = EQ
andmconcat (zipWith compare xs ys)
- instances for container types
- defined in
- other monoidial classes
Alternative
forApplicative
functors with monoid structure
MonadPlus
for monads with a monoid structure (monads which support "choice and failure")
ArrowZero
andArrowPlus
for representingArrows
with monoid structure
6 Foldable
- defined in
Data.Foldable
- for containers that can be "folded" into a summary value
- container-agnostic
- to make an instance either implement
foldMap
orfoldr
foldMap :: Monoid m => (a -> m) -> t a -> m
foldr :: (a -> b -> b) -> b -> t a -> b
- instances
List
Maybe
Array
- containers like
Map
,Set
,Tree
- module contains generalized versions of
Prelude
functions likeconcat
,any
,all
etc.
- generic functions to work with
Applicative
orMonad
instances liketraverse_
,sequenceA_
etc.
Foldable
operations forget structure of container type, butTraversable
will preserve it
7 Traversable
- defined in
Data.Traversable
- every
Traversable
is a foldable functor
- instances only need to implement
traverse
andsequenceA
sequenceA
is the key method ofTraversable
sequenceA :: Applicative f => t (f a) -> f (t a)
- answers the question when we can commute two functors
- ability to compose two mondas depends crucially on this
- instances
[]
Maybe
Map
Tree
Sequence
Set
is notTraversable
even though it'sFoldable
Traversable
andFunctor
instances are almost identical, butTraversable
takes place within anApplicative
context
- any
Traversable
functor is alsoFoldable
and aFunctor
, both classes can be implemented only with methods fromTraversable
8 Category
- fairly new addition to Haskell standard library
- generalizes notion of function compositions to general "morphisms"
- defined in
Control.Category
- instance of
Category
should be something of kind* -> * -> *
(a type constructor which takes 2 type arguments)
- instances
(->)
Kleisli
- methods
id :: cat a a
(.) :: cat b c -> cat a b -> cat a c
- law
id
and(.)
should form a monoid (id
should be identity of(.)
and(.)
should be associative)
- two additional operators
(<<<)
, a synonym for(.)
(>>>)
, is(.)
with its arguments reversed
- can only represent categories whose objects are objects of
Hask
, more general category treatment incategory-extras
9 Arrow
- another abstraction of computation, like
Monad
andApplicative
- type of an
Arrow
computation reflects both its input and output
Arrows
generalize functions, may represent some sort of "effectful" computation
Category
class constraint, so we get identity arrows and arrow composition for free
- methods that need to be implement
arr :: (b -> c) -> (b ~> c)
: takes any functionb -> c
and turns it into a generalized arrowb ~> c
. Says we can treat any function as an arrow.
first :: (b ~> c) -> ((b, d) ~> (c, d))
: turns any arrow fromb
toc
into an arrow from(b,d)
to(c,d)
. Processes first element of a tuple while leaving the second one unchanged
- further methods
second :: (b ~> c) -> ((d, b) ~> (d, c))
: similar tofirst
, but with elements of tuple swapped
(***) :: (b ~> c) -> (b’ ~> c’) -> ((b, b’) ~> (c, c’))
: "parallel composition" of arrows; takes two arrows and turns them into one on tuples (first arrow on first element, second arrow on second element)
(&&&) :: (b ~> c) -> (b ~> c’) -> (b ~> (c, c’))
: "fanout composition"; takes two arrows and makes them into a new arrow which supplies its input to both arrows, returning their results as a tuple
- instances
- only two instances in library
(->)
, the normal function constructor
Kleisli m
which makes functions of typea -> m b
intoArrows
for anyMonad m
- only two instances in library
- laws
arr id = id
arr (h . g) = arr g >>> arr h
first (arr g) = arr (g *** id)
first (g >>> h) = first g >>> first h
first g >>> arr (id ***h ) = arr (id *** h) >>> first g
first g >>> arr fst = arr fst >>> g
first (first g) >>> arr assoc = arr assoc >>> first g
assoc ((x,y),z) = (x,(y,z))
10 ArrowChoice
- allows for alternate execution paths based on intermediate results
- methods
left :: (b ~> c) -> (Either b d ~> Either c d)
right :: (b ~> c) -> (Either d b ~> Either d c)
(+++) :: (b ~> c) -> (b’ ~> c’) -> (Either b b’ ~> Either c c')
(|||) :: (b ~> d) -> (c ~> d) -> (Either b c ~> d)
- similar to
first
,second
,(***)
and(&&&)
, but operating on sum types instead of product types (tuples)
- operate on arrows whose inputs are tagged with
Left
andRight
and choose how to act based on that
left g
has the behavior ofg
for inputs tagged withLeft
and behaves as the identity for inputs tagged asRight
right g
is the mirror image ofleft
(***)
performs "multiplexing":g
behaves asg
for inputs tagged asLeft
andh
behaves ash
for inputs tagged asRight
; tags are preserved
(|||)
is "merge" or "fanin": like(***)
but tags are discarded; mnemonic:g ||| h
performs eitherg
orh
.
11 ArrowApply
- more flexible than
ArrowChoice
- can compute an arrow from intermediate results and this computed arrow continues the computation
- method
app :: (b ~> c, b) ~> c
- allows us to apply a computed arrow to an input
- this is exactly what monadic bin (
(>>=)
) does
ArrowApply
andMonad
are equivalent in expressive power
- any instance of
ArrowApply
can be made aMonad
viaArrowMonad
12 ArrowLoop
- describes arrows that can use recursion to compute results
- used to desugar
rec
construct in arrow notation
- GHC has a special arrow notation similar to do-notation that allows to assign names to intermediate results
13 Comonad
- categorical dual of
Monad
extract
is the dual ofreturn
duplicate
is the dual ofjoin
(adds layer of monadic wrapping)
extend
is the dual of(>>=)
with arguments in different order
- can be defined by
fmap
,extract
and eitherduplicate
orextend