A monad is a really simple concept.
Why do we have functions? Can’t we just write one big program, with loops, if statements, expressions, assignments?
Why do we need functions? We really need functions so that we can structure our programs. We need functions so that we can
decompose the program into smaller pieces and recompose it. This is what we have been talking about in category theory from the very
beginning: it is composition.
And the power of functions really is in the dot. That’s where the power sits. Dot is the composition operator in Haskell.
It combines two functions so the output of one function becomes the input of the other.
So that explains what functions are really, functions are about composition.
And so is the monad. People start by giving examples of monads, there is the state monad, there is the exception monad, these are so
completely different, what do exceptions have to do with state? What do they have to do with input/output?
Well, it’s just as with functions: functions can be used to implement so many different things, but really functons are about composition.
And so is the monad. The monad is all about composing stuff. It replaces this dot with the Kleisli arrow…The fish operator.
Dot is used for composing simple functions in which the output of one function matches the input of another function, and that’s the
most trivial way of composing stuff.
The fish operator is used to compose these functions whose output type is embellished. So if the output of a function would be B but
now we are embellishing it with some stuff, e.g. embellishing it with logging, by adding a string to it, the logging kleisli arrow, but then in
order to compose these things we have to unpack the return type before we can send it on to the next function. So actually not much is
happening inside the dot, a function is called and the result is passed to another function, but much more is happening inside the fish,
because there is the unpacking and the passing of the value to the next function, and also maybe some decision is taken, like in the
case of exceptions. Once we have this additional step of combining functions, we can make decisions, like maybe we don’t want to call
the next function at all, maybe we want to bypass it. So a lot of stuff may happen inside the fish.
And just like we have the identity function here, that’s an identity with respect to the dot, here we have this kleisli arrow that
represents identity, that returns this embellished result, but of the same type, and we call it return in Haskell. And it is called return
because at some point you want to be able to program like an imperative programmer. So it’s not that imperative programming is bad,
imperative programming could be good, as long as it is well controlled, and the monad lets you do programming that is kind of imperative
style. You don’t have to do this, but sometimes it is easier to understand your code when you write it in imperative style, even though it is
immediately translated into this style of composing functions. So this is just for our convenience, we want to be able to write something
that looks more imperative, but behind the scene it is still function composition upon function composition
Bartosz	Milewski introduces	the	need	for	Kleisli	composition,	in	his	lecture	on	Monads		
Category	Theory	10.1:	Monads @BartoszMilewski
That’s our problem. So what can we do?
What would it take to make them compose?
Here is our function diagram for pure function composition. And if we sort of replace things with effectful
functions, they look like this, so we have something like andThen, looks something like a fish, and every type
has an id, we are calling it pure. If we were able to define this and make it compose then we would get that
power that we were talking about. So how do we write this in Scala?
We can implement compose, the fish operator using flatMap, so the fish
operator is something we can derive later really, the operation we need is
flatMap.
Rob Norris
@tpolecat
Let’s recap, using material by Rob Norris, why we
need Kleisli composition (the fish operator),
which is defined in terms of flatMap (aka bind)
scale.bythebay.io:	Rob	Norris,	Functional	Programming	with	Effects
And there are lots of different
kinds of Functors like this, but I
want to also point out that with
functions, I am really talking
about pure functions.
Because composition breaks down if we have side effects. It no longer works. And so
what we want to do is we want to track the effects in the return type of the function.
Rather than having side effects, like returning nulls or throwing exceptions, or
something, we are going to track them in the return type of the function.
So here the effect is that the function f might
not return a B, it might return just a None.
Rúnar	Bjarnason	
@runarorama
But we run into a problem when we have functions
of this form, that we can no longer use regular
function composition. Like we can’t say f andThen
g, if we have both f and g that return Option,
because the types are no longer compatible.
But we can solve that just by writing a little more code. So we can say f andThen this
function that flatMaps g over the result of f. So we can actually write a composition
on these types of functions, that is not ordinary function composition, it is
composition on function and some additional structure.
But we can actually write that as an operator, and in both Scalaz and Cats it is
represented as this sort of fish operator >=>.
So if we have f that goes from A to Option[B] and g that goes from B to Option[C] we
have a composite function f fish g, that goes from A to Option[C].
And now this is starting to look like real composition.
And	in	fact,	once	
we	have	this,	we	
have	a	category.	
And	this	thing	is	
called	a	Kleisli
category,	named	
after	a	
mathematician	
called	Heinrich	
Kleisli.
So in general we have a Kleisli category like this, exactly
when the Functor M is a Monad.
And when we have this kind of thing, we have a Monad.
Let’s recap, using material by Rúnar
Bjarnason, why we need Kleisli
composition (the fish operator), which is
defined in terms of flatMap (aka bind)
Scala eXchange 2017 Keynote:
Composing Programs
https://skillsmatter.com/skillscasts/10746-keynote-composing-programs
If you ask someone to do monadic programming using just the fish operator (Kleisli
composition), that’s equivalent to using point-free style, and that is hard, and not very
readable. So the definition of monad using the fish operator is not the main definition used in
programming languages. And I’ll show you how to get from one definition to another very
easily, and I will call this next segment Fish Anatomy.
…
The fish operator >=> can be defined in terms of the bind operator >>=
…
So we have simplified the problem. We still have to implement bind
…
The interface of >=> is very symmetric, has meaning, looks very much like function composition.
>>= not so much.
…
So a lot of people will start by saying a monad is something that has this bind operator, and
then you ask yourself whoever came up with this weird signature of >>= ?
And it is not really weird, it comes from this [process we went through].
Bartosz	Milewski defines	Kleisli	composition	in	terms	of	bind,	in	his	lecture	on	Monads		
Category	Theory	10.1:	Monads @BartoszMilewski
class Monad m where
(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
return :: a -> m a
trait Monad[F[_]] {
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C]
def unit[A](a: => A): F[A]
}
Kleisli	composition	+	unit Kleisli	composition	+	return
Kleisli Composition (fish operator) >=> compose
Bind >>= flatMap
lifts a to m a (lifts A to F[A]) return unit/pure
(>=>)::(a ->mb)->(b->mb)->(a->mc)
(>=>) = a -> (f a) >>= g
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C]
a => flatMap(f(a))(g)
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
-- can then implement Kleisli composition using bind
(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
(>=>) = a -> (f a) >>= g
trait Monad[F[_]] {
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
def unit[A](a: => A): F[A]
// can then implement Kleisli composition using flatMap
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] =
a => flatMap(f(a))(g)
}
flatMap	+	unit bind	+	return	(Kleisli	composition	can	then	be	implemented	with	bind)
Defining	a	Monad	in	terms	of	Kleisli	composition	and	Kleisli	identity	function
Defining	Kleisli	composition	in	terms	of	flatMap	(bind)
Defining	a	Monad	in	terms	of	flatmap	(bind)	and	unit	(return)
Bartosz	Milewski	introduces	a	third	definition	of	Monad in	terms	of	join	and	return,	based	on	Functor	
So this (join and return) is an alternative definition of a monad. But in this case I have to specifically say that m is a Functor, which is actually a nice thing, that I have to explicitly specify it.
…
But remember, in this case (join and return) you really have to assume that it is a functor. In this way, join is the most basic thing. Using just join and return is really more atomic than using
either bind or the Kleisli arrow, because they additionally susbsume functoriality, whereas here, functoriality is separate, separately it is a functor and separately we define join, and
separately we define return.
…
So this definition (join and return) or the definition with the Kleisli arrow, they are not used in Haskell, although they could have been. But Haskell people decided to use this (>>= and return)
as their basic definition and then for every monad they separately define join and the Kleisli arrow. So if you have a monad you can use join and the Kleisli arrow because they are defined in
the library for you. So it’s always enough to define just bind, and then fish and join will be automatically defined for you, you don’t have to do it.
#1
#2
#3
Category	Theory	10.1:	Monads @BartoszMilewski
Functional Programming in Scala
In	’FP	in	Scala’	we	also	see	a	third	minimal	sets	of	primitive	Monad combinators
We’ve seen three minimal sets of primitive Monad combinators, and instances of
Monad will have to provide implementations of one of these sets:
• unit and flatMap
• unit and compose
• unit , map , and join
And we know that there are two monad laws to be satisfied, associativity and identity,
that can be formulated in various ways. So we can state plainly what a monad is :
A monad is an implementation of one of the minimal sets of monadic
combinators, satisfying the laws of associativity and identity.
That’s a perfectly respectable, precise, and terse definition. And if we’re being precise,
this is the only correct definition.
(by	Paul	Chiusano	and	Runar	Bjarnason)
@pchiusano @runarorama
trait Monad[F[_]] {
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
def unit[A](a: => A): F[A]
}
class Monad m where
(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
return :: a -> m a
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor m => Monad m where
join :: m(m a) -> ma
return :: a -> m a
trait Functor[F[_]] {
def map[A,B](m: F[A])(f: A => B): F[B]
}
trait Monad[F[_]] extends Functor[F] {
def join[A](mma: F[F[A]]): F[A]
def unit[A](a: => A): F[A]
}
trait Monad[F[_]] {
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C]
def unit[A](a: => A): F[A]
}
flatmap	+	unit
map +	join +	unit
Kleisli	composition	+	unit
bind	+	return
fmap	+	join +	return
Kleisli	composition	+	return
Defining	a	Monad	in	terms	of	map	(fmap),	join	and	unit	(return)
trait Monad[F[_]] {
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
def unit[A](a: => A): F[A]
def join[A](mma: F[F[A]]): F[A] = flatMap(mma)(ma => ma)
def map[A,B](m: F[A])(f: A => B): F[B] = flatMap(m)(a => unit(f(a)))
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] = a => flatMap(f(a))(g)
}
trait Functor[F[_]] {
def map[A,B](m: F[A])(f: A => B): F[B]
}
trait Monad[F[_]] extends Functor[F] {
def join[A](mma: F[F[A]]): F[A]
def unit[A](a: => A): F[A]
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B] = join(map(ma)(f))
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] = a => flatMap(f(a))(g)
}
trait Monad[F[_]] {
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C]
def unit[A](a: => A): F[A]
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B] = compose((_:Unit) => ma, f)(())
def map[A,B](m: F[A])(f: A => B): F[B] = flatMap(m)(a => unit(f(a)))
}
flatmap +	unit
map +	join +	unit
Kleisli	composition	+	unit
Functional Programming in Scala
defining	join,	map and	compose in	
terms	of	flatMap and	unit
defining	flatMap and	map in	terms	
of	compose	and	unit
defining	flatMap and	compose	in	
terms	of	join	and	map
Using primitive monad
combinators to define the
other key combinators
(by	Paul	Chiusano	and	Runar	Bjarnason)
@pchiusano @runarorama
Monads in Category Theory
In Category Theory, a Monad is a functor equipped with a pair of natural transformations satisfying the laws
of associativity and identity.
What does this mean? If we restrict ourselves to the category of Scala types (with Scala types as the objects
and functions as the arrows), we can state this in Scala terms.
A Functor is just a type constructor for which map can be implemented:
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B): F[B]
}
A natural transformation from a functor F to a functor G is just a polymorphic function:
trait Transform[F[_], G[_]] {
def apply[A](fa: F[A]): G[A]
}
The natural transformations that form a monad for F are unit and join:
type Id[A] = A
def unit[F](implicit F: Monad[F]) = new Transform[Id, F] {
def apply(a: A): F[A] = F.unit(a)
}
def join[F](implicit F: Monad[F]) = new Transform[({type f[x] = F[F[x]]})#f, F] {
def apply(ffa: F[F[A]]): F[A] = F.join(ffa)
}
In	Category	Theory	a	Monad	is	a	functor	equipped	with	a	pair	of	natural	transformations satisfying	the	laws	of	associativity	and	identity
(by	Runar	Bjarnason)
@runarorama
All three definitions [of Monad] are used in category theory, but really, everybody uses the one with join, except that they don’t call it join and they don’t call it return. And they don’t call the functor M, they
call it T. So the translation is m goes to T, join goes to µ and return goes to η. They use Greek letters here and it makes sesne because they use Greek letters for natural transformations and you will see that
these (join and return) are natural transformations. So now I’ll switch notation to T, µ and η.
We already talked about return at some point, when I talked about natural transformations, I said that return really is a polymorphic function that goes from a to ma, in the old notation, but really, since it is a
polymorphic function, it is really a natural transformation a --> ma where a is the identity functor acting on a (Ida). So it is really a natural transformation between two functors, and Ida is a component of the
natural transformation, for some particular a. So return is a component of the natural transformation from the identity functor to m, and since we don‘t want to use m here, I am going to use T.
So we‘ll say that η (unit/return) is a natural transformation from the identity functor to T. And it means the same thing, except that in Haskell we use it in components, every natural transformation has
components, so for a particular a, Ida acting on a gives you a, T acting on a gives you ma.
Now what is µ?, µ (join) is also a natural transformation. Remember, join was going from m(m a) to ma. What is m(m a)? It means we take this functor, we act on a, and then we apply it to the result. So this is
double application of the functor. It is just composition of the functor with itself. So this in mathematical notation would be T ∘ T --> T. Double application ot T, in components, it will give you m(m a), double
application of a functor in components is m(m a). Single application of the functor is m a. It is a natural transformation.
So in category theory we have to say it is a natural transformation. In Haskell we didn‘t say it is natural transformation, we didn‘t mention the naturality condition for return. Why? Because of polymorphism,
because of ‘theorems for free‘, it is automatically a natural transformation, the naturality condition is automatic. But in category theory we have to say it is a natural transformation. So T ∘ T is usually written
simply as T2. T2 is just compositon of T with itself. That‘s shorthand notation.
So, a monad is a functor T and two natural transformations. Plus some laws, otherwise if we try to make a Kleisli category on top of this we wouldn‘t get associativity and identity laws.
Bartosz	Milewski	defines	a	monad	as	a	functor	and	two	natural	transformations,	plus	associativity	and	identity	laws
Category	Theory	10.1:	Monads
@BartoszMilewski
A B
f
FA FB
F(f)
GA GB
G(f)
F
G
C1
C1 and C2 are categories and ∘	denotes their composition operations.
F and G are functors from C1 to C2 which map each C1 object to a C2 object and map each C1 arrow to a C2 arrow
A natural transformation 𝜏	from F to G (either both covariant of both contravariant) is
a family of arrows 𝜏 𝑋: FX →	GX of C2 indexed by the object X of C1 such that for each arrow f: A →	B of C1, the appropriate square
in C2 commutes (depending on the variance)
Natural	Transformation
𝜏A 𝜏B
G(f)∘ 𝜏A
𝜏B∘F(f)
C2
𝜏
F G
natural	transformation	
𝜏	from	F to	G
𝜏A
FA GA
GB
𝜏B
GZ
𝜏Z
…… …
FA FB
F(f)
GA GB
G(f)
𝜏A 𝜏B
F(f)∘ 𝜏A
𝜏B∘G(f)
FB
FZ
covariant contravariant
the	square	commutes
G(f)∘ 𝜏A= 𝜏B∘F(f)
𝜏
F G
Naturality	
Condition
Let’s	recap	on	Natural	Transformations	(3	slides)
https://www.slideshare.net/pjschwarz/natural-transformations
@philip_schwarz
A B
f
F[A]
f↑F
f↑G
𝜏A 𝜏B
f↑G∘ 𝜏A
𝜏B∘f↑F
𝜏
F G
natural	transformation	
𝜏	from	F to	G
𝜏A
F[A] G[A]
F[B]
𝜏B
F[Z]
𝜏Z
…… …
F[A] F[B]
f↑F
G[A] G[B]
f↑G
𝜏A 𝜏B
F[B]
G[A] G[B]
G[B]
G[Z]
F(f)∘ 𝜏A
𝜏B∘G(f)
F
G
C1	=	C2	=	Scala	types	and	functions
• objects:	types
• arrows:	functions
• composition	operation:		compose function,	denoted	here	by	∘
• identity	arrows:	identity	function	T	=>	T
Functor F from	C1	to	C2	consisting	of	
• type	constructor	F that	maps	type	A	to	F[A]	
• a	map function	from	function	f:A=>B	to	function	f↑F	:F[A]	=>	F[B]	
Functor G from	C1	to	C2	consisting	of	
• type	constructor	G that	maps	type	A	to	G[A]	
• a	map function	from	function	f:A=>B	to	function	f↑G	:G[A]	=>	G[B]	
F[A] is	type	A	lifted	into	context	F
f↑F		is	function	f	lifted	into	context	F
Generic	Scala	Example:	Natural	Transformation	between	two	Functors	from	the	category	of	‘Scala	types	and	functions’	to	itself
the	square	commutes
f↑G	∘ 𝜏A = 𝜏B ∘	f↑F
mapG f ∘ 𝜏A = 𝜏B ∘ mapF f
𝜏
F G
covariant contravariant
map lifts	f	into	F
f↑F		is	map f
C1 = C2 = types	and	functions Naturality	
Condition
https://www.slideshare.net/pjschwarz/natural-transformations
@philip_schwarz
String
length
List[String] List[Int]
length↑List
Option[String] Option[Int]
Concrete	Scala	Example:	safeHead - natural	transformation	𝜏	from	List functor	to	Option functor
safeHead[String] =	𝜏StringInt
length↑Option
𝜏Int	=	safeHead[Int]
safeHead ∘length↑Option
Option
𝜏
List Option
natural	transformation	𝜏	from	List to	Option
𝜏String
List[String] Option[String]
List[Int]
𝜏Int
List[Char]
𝜏Char
…… …
Option[Int]
Option[Char]
length↑List	∘	safeHead
covariant
val length: String => Int = s => s.length
// a natural transformation
def safeHead[A]: List[A] => Option[A] = {
case head::_ => Some(head)
case Nil => None
}
the	square	commutes
length↑List	∘ safeHead = safeHead	∘	length↑Option
(mapList length)			∘ safeHead = safeHead	∘	(mapOption length)
𝜏
List Option
F[A] is	type	A	lifted	into	context	F
f↑F		is	function	f	lifted	into	context	F
map lifts	f	into	F
f↑F		is	map f
C1 = C2 = types	and	functions
List
Naturality	
Condition
https://www.slideshare.net/pjschwarz/natural-transformations
@philip_schwarz
FA FB
A B
FFA FFB
FA FB
GA GB
FA FB A
join join
unit
𝜏A
f
f↑F
(f↑F)↑F
f↑F
f↑G
(f↑F)↑F ∘ unit
unit ∘f↑F
𝜏B
trait Functor[F[_]] {
def map[A, B](f: A => B): F[A] => F[B]
}
trait Monad[F[_]] extends Functor[F] {
def join[A](mma: F[F[A]]): F[A]
def unit[A](a: => A): F[A]
def flatMap[A,B](f: A => F[B]): F[A] => F[B] = (ma:F[A]) => join(map(f)(ma))
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] = a => flatMap(g)(f(a))
}
unit
unit unit
gg↑F
f↑F ∘ unit
unit ∘ f
f↑F ∘ join
join ∘(f↑F)↑F
f↑G∘ 𝜏A
𝜏B ∘	f↑F
flatMap g = join ∘ (map g)
= join ∘ g↑F
flatMap	g
map	g
FB A
FC
B g
h g compose h
calls	g	with	A,	then	flatMaps	h	over	
result	FB	(to	apply	h	to	its	contents),	
returning	result	FC.
f									pure function
g,h					effectful (kleisli)	functions
flatMap >>=
compose <=<
map <$>
natural	transformations:	
𝜏	
unit	aka	pure aka	η
join	aka	flatten aka	μ
map lifts	f	into	F
f↑F		is	map f
Note:	signatures	of	map	and	flatMap	
have	here	been	rearranged	(by	
swapping	parameters	around	and	
uncurrying):	they	now	return	a	function
Monadic	Combinators	in	Action	
primitive:	unit and	join – natural	transformations
derived:	flatMap and	compose	(Kleisli	composition)
Creates	a	function	which	
flatMap maps	given	function	over	its	argument	and	joins	the	result
compose calls	1st given	function	with	its	argument	and	flatMaps	
2nd given	function	over	the	result
maps	g	onto	FA,	then	flattens	
resulting	FFB,	returning	FA
FC
(flatMap h)	∘ g
flatMap	h
=		(flatMap h)	∘ gg compose h
List[String] List[int]
String Int
List[List[String]] List[List[Int]]
List[String] List[int]
Option[String] Option[Int]
List[String] List[Int] String
join join
unit
safeHeadList
length
length↑L
(length↑L)↑L
length↑L
length↑O
(length↑L)↑L ∘ unit
unit ∘length↑L
unit
unit unit
asciiascii	↑L
length↑L ∘ unit
unit ∘ length
length↑L ∘ join
join ∘(length↑L)↑L
length↑O∘ safeHeadList
safeHeadOption ∘	length↑L
flatMap ascii = join ∘ (map ascii)
= join ∘ ascii↑L
flatMap	ascii
map	ascii
calls	ascii with	a	String,	then	flatMaps	chars onto	result	List[Int]	
(producing	a	Char	for	each	Int),	returning	result	List[Char]
natural	transformations:	
safeHead	
unit	aka	pure aka	η
join	aka	flatten aka	μ
map lifts	f	into	F
f↑L		is	map f	for	F=List
f↑O		is	map f	for	F=Option
Concrete	Example	of	Monadic	Combinators	in	Action	
primitive:	unit	and	join	– natural	transformations
derived:	flatMap and	compose (Kleisli	composition)
Creates	a	function	which	
flatMap maps	given	function	over	its	argument	and	joins	the	result
compose calls	1st given	function	with	its	argument	and	flatMaps	
2nd given	function	over	the	result
maps	ascii onto	List[String],	then	flattens	
resulting	List[List[Int]],	returning	List[Int]
safeHeadOption
// a natural transformation
def safeHead[A]: List[A] => Option[A] = {
case head::_ => Some(head)
case Nil => None
}
// an effectful (Kleisli) function -
def ascii(s:String): List[Int] =
if(s.isEmpty) Nil else s.head.toInt :: ascii(s.tail)
// another effectful function -
def chars(n:Int): List[Char] = n.toString.toList
// a pure function
val length: String => Int =
s => s.length
List[Int] String
Int ascii
chars ascii compose chars
List[Char]
(flatMap chars)	∘ ascii
flatMap	chars
=		(flatMap chars)	∘ asciiascii compose chars
ascii("Abc") == List(65,98,99)
chars(65) == List('6','5')
List[Char]
ascii("Abc") == List(65,98,99)
chars(65) == List('6','5')
(ascii compose chars)("Abc") = List('6','5','9','8','9','9')
def safeHead[A]: List[A] => Option[A] = {
case head::_ => Some(head)
case Nil => None
}
def ascii(s:String):List[Int] =
if(s.isEmpty) Nil else s.head.toInt :: ascii(s.tail)
def chars(n:Int):List[Char] =
n.toString.toList
assert(ascii("Abc") == List(65,98,99))
assert(chars(65) == List('6','5'))
val listM = new Monad[List] {
def map[A, B](f: A => B): List[A] => List[B] = {
case head :: tail => f(head) :: map(f)(tail)
case Nil => Nil
}
def unit[A](a: => A): List[A] = List(a)
def join[A](mma: List[List[A]]): List[A] =
mma match {
case head::tail => head ::: join(tail)
case Nil => Nil
}
}
val optionM = new Monad[Option] {
def map[A,B](f: A => B): Option[A] => Option[B] = {
case Some(a) => Some(f(a))
case None => None
}
def unit[A](a: => A): Option[A] = Some(a)
def join[A](mma: Option[Option[A]]): Option[A] =
mma match {
case Some(a) => a
case None => None
}
}
trait Functor[F[_]] {
def map[A, B](f: A => B): F[A] => F[B]
}
trait Monad[F[_]] extends Functor[F] {
def join[A](mma: F[F[A]]): F[A]
def unit[A](a: => A): F[A]
def flatMap[A,B](f: A => F[B]): F[A] => F[B] =
(ma:F[A]) => join(map(f)(ma))
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] =
a => flatMap(g)(f(a))
}
val length: String => Int = s => s.length
val lengthLiftedOnce: List[String] => List[Int] = (listM map length)
val lengthLiftedTwice: List[List[String]] => List[List[Int]] = listM map lengthLiftedOnce
assert(length("abcd") == 4)
assert(lengthLiftedOnce(List("abcd","efg","hi")) == List(4,3,2))
assert(lengthLiftedTwice(List(List("abcd","efg","hi"),List("jkl","mo","p"))) == List(List(4,3,2),List(3,2,1)))
val computeAndThenTransform: List[List[String]] => List[Int] = (listM.join[Int](_)) compose lengthLiftedTwice
val transformAndThenCompute: List[List[String]] => List[Int] = lengthLiftedOnce compose listM.join
assert(computeAndThenTransform(List(List("abcd","efg","hi"),List("jkl","mo","p"))) ==
transformAndThenCompute(List(List("abcd","efg","hi"),List("jkl","mo","p"))))
assert(computeAndThenTransform(List(List("abcd","efg","hi"),List("jkl","mo","p"))) == List(4,3,2,3,2,1))
assert(transformAndThenCompute(List(List("abcd","efg","hi"),List("jkl","mo","p"))) == List(4,3,2,3,2,1))
val computeAndThenTransform: List[String] => List[List[Int]] =
((x:List[Int]) => listM.unit(x)) compose lengthLiftedOnce
val transformAndThenCompute: List[String] => List[List[Int]] =
lengthLiftedTwice compose ((x:List[String]) => listM.unit(x))
assert(computeAndThenTransform(List("abcd","efg","hi")) == transformAndThenCompute(List("abcd","efg","hi")))
assert(computeAndThenTransform(List("abcd","efg","hi")) == List(List(4,3,2)))
assert(transformAndThenCompute(List("abcd","efg","hi")) == List(List(4,3,2)))
val computeAndThenTransform: String => List[Int] = ((x:Int) => listM.unit(x)) compose length
val transformAndThenCompute: String => List[Int] = lengthLiftedOnce compose ((x:String) => listM.unit(x))
assert(computeAndThenTransform("abcd") == transformAndThenCompute("abcd"))
assert(computeAndThenTransform("abcd") == List(4))
assert(transformAndThenCompute("abcd") == List(4))
List[String] List[int]
String Int
List[List[String]] List[List[Int]]
List[String] List[int]
join join
unit
length
length↑L
(length↑L)↑L
(length↑L)↑L ∘ unit
unit ∘length↑L
unit
unit unit
length↑L ∘ unit
unit ∘ length
length↑L ∘ join
join ∘(length↑L)↑L
val computeAndThenTransform: List[String] => Option[Int] = safeHead compose (listM map length)
val transformAndThenCompute: List[String] => Option[Int] = (optionM map length) compose safeHead
assert(computeAndThenTransform(List("abc", "d", "ef")) == transformAndThenCompute(List("abc", "d", "ef")))
assert(computeAndThenTransform(List("abc", "d", "ef")) == Some(3))
assert(transformAndThenCompute(List("abc", "d", "ef")) == Some(3))
assert(computeAndThenTransform(List()) == transformAndThenCompute(List()))
assert(computeAndThenTransform(List()) == None)
assert(transformAndThenCompute(List()) == None)
Option[String] Option[Int]
safeHeadList
length↑O
length↑O∘ safeHeadList
safeHeadOption ∘	length↑L
safeHeadOption
List[List[String]] List[List[Int]]
List[String] List[int]
List[String] List[Int] String
join join
(length↑L)↑L
length↑L
asciiascii	↑L
length↑L ∘ join
join ∘(length↑L)↑L
flatMap ascii = join ∘ (map ascii)
= join ∘ ascii↑L
flatMap	ascii
map	ascii
maps	ascii onto	List[String],	then	flattens	
resulting	List[List[Int]],	returning	List[Int]
def ascii(s:String):List[Int] = if(s.isEmpty) Nil else s.head.toInt :: ascii(s.tail)
assert(ascii("Abc") == List(65,98,99))
val mappedAscii: List[String] => List[List[Int]] = listM map ascii
val flatMappedAscii: List[String] => List[Int] = listM flatMap ascii
val compositionOfMappedAsciiAndJoin = (listM.join[Int](_)) compose mappedAscii
assert( compositionOfMappedAsciiAndJoin(List("abcd","efg","hi")) == flatMappedAscii(List("abcd","efg","hi")) )
assert( compositionOfMappedAsciiAndJoin(List("abcd","efg","hi")) == List(97, 98, 99, 100,101,102,103,104,105))
assert( flatMappedAscii(List("abcd","efg","hi")) == List(97, 98, 99, 100,101,102,103,104,105))
def ascii(s:String):List[Int] = if(s.isEmpty) Nil else s.head.toInt :: ascii(s.tail)
def chars(n:Int):List[Char] = n.toString.toList
assert(chars(65) == List('6','5'))
assert(ascii("Abc") == List(65,98,99))
def flatMappedChars = listM flatMap chars
def kleisliCompositionOfAsciiAndChars = listM.compose(ascii, chars)
assert( kleisliCompositionOfAsciiAndChars("Abc") == (flatMappedChars compose ascii)("Abc"))
assert( kleisliCompositionOfAsciiAndChars("Abc") == List('6','5','9','8','9','9'))
assert( (flatMappedChars compose ascii)("Abc") == List('6','5','9','8','9','9'))
calls	ascii with	a	String,	then	flatMaps	chars onto	result	List[Int]	
(producing	a	Char	for	each	Int),	returning	result	List[Char]
List[Int] String
Int ascii
chars ascii compose chars
List[Char]
(flatMap chars)	∘ ascii
flatMap	chars
=		(flatMap chars)	∘ asciiascii compose chars
List[Char]

Monad as functor with pair of natural transformations

  • 1.
    A monad isa really simple concept. Why do we have functions? Can’t we just write one big program, with loops, if statements, expressions, assignments? Why do we need functions? We really need functions so that we can structure our programs. We need functions so that we can decompose the program into smaller pieces and recompose it. This is what we have been talking about in category theory from the very beginning: it is composition. And the power of functions really is in the dot. That’s where the power sits. Dot is the composition operator in Haskell. It combines two functions so the output of one function becomes the input of the other. So that explains what functions are really, functions are about composition. And so is the monad. People start by giving examples of monads, there is the state monad, there is the exception monad, these are so completely different, what do exceptions have to do with state? What do they have to do with input/output? Well, it’s just as with functions: functions can be used to implement so many different things, but really functons are about composition. And so is the monad. The monad is all about composing stuff. It replaces this dot with the Kleisli arrow…The fish operator. Dot is used for composing simple functions in which the output of one function matches the input of another function, and that’s the most trivial way of composing stuff. The fish operator is used to compose these functions whose output type is embellished. So if the output of a function would be B but now we are embellishing it with some stuff, e.g. embellishing it with logging, by adding a string to it, the logging kleisli arrow, but then in order to compose these things we have to unpack the return type before we can send it on to the next function. So actually not much is happening inside the dot, a function is called and the result is passed to another function, but much more is happening inside the fish, because there is the unpacking and the passing of the value to the next function, and also maybe some decision is taken, like in the case of exceptions. Once we have this additional step of combining functions, we can make decisions, like maybe we don’t want to call the next function at all, maybe we want to bypass it. So a lot of stuff may happen inside the fish. And just like we have the identity function here, that’s an identity with respect to the dot, here we have this kleisli arrow that represents identity, that returns this embellished result, but of the same type, and we call it return in Haskell. And it is called return because at some point you want to be able to program like an imperative programmer. So it’s not that imperative programming is bad, imperative programming could be good, as long as it is well controlled, and the monad lets you do programming that is kind of imperative style. You don’t have to do this, but sometimes it is easier to understand your code when you write it in imperative style, even though it is immediately translated into this style of composing functions. So this is just for our convenience, we want to be able to write something that looks more imperative, but behind the scene it is still function composition upon function composition Bartosz Milewski introduces the need for Kleisli composition, in his lecture on Monads Category Theory 10.1: Monads @BartoszMilewski
  • 2.
    That’s our problem.So what can we do? What would it take to make them compose? Here is our function diagram for pure function composition. And if we sort of replace things with effectful functions, they look like this, so we have something like andThen, looks something like a fish, and every type has an id, we are calling it pure. If we were able to define this and make it compose then we would get that power that we were talking about. So how do we write this in Scala? We can implement compose, the fish operator using flatMap, so the fish operator is something we can derive later really, the operation we need is flatMap. Rob Norris @tpolecat Let’s recap, using material by Rob Norris, why we need Kleisli composition (the fish operator), which is defined in terms of flatMap (aka bind) scale.bythebay.io: Rob Norris, Functional Programming with Effects
  • 3.
    And there arelots of different kinds of Functors like this, but I want to also point out that with functions, I am really talking about pure functions. Because composition breaks down if we have side effects. It no longer works. And so what we want to do is we want to track the effects in the return type of the function. Rather than having side effects, like returning nulls or throwing exceptions, or something, we are going to track them in the return type of the function. So here the effect is that the function f might not return a B, it might return just a None. Rúnar Bjarnason @runarorama But we run into a problem when we have functions of this form, that we can no longer use regular function composition. Like we can’t say f andThen g, if we have both f and g that return Option, because the types are no longer compatible. But we can solve that just by writing a little more code. So we can say f andThen this function that flatMaps g over the result of f. So we can actually write a composition on these types of functions, that is not ordinary function composition, it is composition on function and some additional structure. But we can actually write that as an operator, and in both Scalaz and Cats it is represented as this sort of fish operator >=>. So if we have f that goes from A to Option[B] and g that goes from B to Option[C] we have a composite function f fish g, that goes from A to Option[C]. And now this is starting to look like real composition. And in fact, once we have this, we have a category. And this thing is called a Kleisli category, named after a mathematician called Heinrich Kleisli. So in general we have a Kleisli category like this, exactly when the Functor M is a Monad. And when we have this kind of thing, we have a Monad. Let’s recap, using material by Rúnar Bjarnason, why we need Kleisli composition (the fish operator), which is defined in terms of flatMap (aka bind) Scala eXchange 2017 Keynote: Composing Programs https://skillsmatter.com/skillscasts/10746-keynote-composing-programs
  • 4.
    If you asksomeone to do monadic programming using just the fish operator (Kleisli composition), that’s equivalent to using point-free style, and that is hard, and not very readable. So the definition of monad using the fish operator is not the main definition used in programming languages. And I’ll show you how to get from one definition to another very easily, and I will call this next segment Fish Anatomy. … The fish operator >=> can be defined in terms of the bind operator >>= … So we have simplified the problem. We still have to implement bind … The interface of >=> is very symmetric, has meaning, looks very much like function composition. >>= not so much. … So a lot of people will start by saying a monad is something that has this bind operator, and then you ask yourself whoever came up with this weird signature of >>= ? And it is not really weird, it comes from this [process we went through]. Bartosz Milewski defines Kleisli composition in terms of bind, in his lecture on Monads Category Theory 10.1: Monads @BartoszMilewski
  • 5.
    class Monad mwhere (>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c) return :: a -> m a trait Monad[F[_]] { def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] def unit[A](a: => A): F[A] } Kleisli composition + unit Kleisli composition + return Kleisli Composition (fish operator) >=> compose Bind >>= flatMap lifts a to m a (lifts A to F[A]) return unit/pure (>=>)::(a ->mb)->(b->mb)->(a->mc) (>=>) = a -> (f a) >>= g def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] a => flatMap(f(a))(g) class Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a -- can then implement Kleisli composition using bind (>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c) (>=>) = a -> (f a) >>= g trait Monad[F[_]] { def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B] def unit[A](a: => A): F[A] // can then implement Kleisli composition using flatMap def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] = a => flatMap(f(a))(g) } flatMap + unit bind + return (Kleisli composition can then be implemented with bind) Defining a Monad in terms of Kleisli composition and Kleisli identity function Defining Kleisli composition in terms of flatMap (bind) Defining a Monad in terms of flatmap (bind) and unit (return)
  • 6.
    Bartosz Milewski introduces a third definition of Monad in terms of join and return, based on Functor So this(join and return) is an alternative definition of a monad. But in this case I have to specifically say that m is a Functor, which is actually a nice thing, that I have to explicitly specify it. … But remember, in this case (join and return) you really have to assume that it is a functor. In this way, join is the most basic thing. Using just join and return is really more atomic than using either bind or the Kleisli arrow, because they additionally susbsume functoriality, whereas here, functoriality is separate, separately it is a functor and separately we define join, and separately we define return. … So this definition (join and return) or the definition with the Kleisli arrow, they are not used in Haskell, although they could have been. But Haskell people decided to use this (>>= and return) as their basic definition and then for every monad they separately define join and the Kleisli arrow. So if you have a monad you can use join and the Kleisli arrow because they are defined in the library for you. So it’s always enough to define just bind, and then fish and join will be automatically defined for you, you don’t have to do it. #1 #2 #3 Category Theory 10.1: Monads @BartoszMilewski
  • 7.
    Functional Programming inScala In ’FP in Scala’ we also see a third minimal sets of primitive Monad combinators We’ve seen three minimal sets of primitive Monad combinators, and instances of Monad will have to provide implementations of one of these sets: • unit and flatMap • unit and compose • unit , map , and join And we know that there are two monad laws to be satisfied, associativity and identity, that can be formulated in various ways. So we can state plainly what a monad is : A monad is an implementation of one of the minimal sets of monadic combinators, satisfying the laws of associativity and identity. That’s a perfectly respectable, precise, and terse definition. And if we’re being precise, this is the only correct definition. (by Paul Chiusano and Runar Bjarnason) @pchiusano @runarorama
  • 8.
    trait Monad[F[_]] { defflatMap[A,B](ma: F[A])(f: A => F[B]): F[B] def unit[A](a: => A): F[A] } class Monad m where (>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c) return :: a -> m a class Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a class Functor f where fmap :: (a -> b) -> f a -> f b class Functor m => Monad m where join :: m(m a) -> ma return :: a -> m a trait Functor[F[_]] { def map[A,B](m: F[A])(f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { def join[A](mma: F[F[A]]): F[A] def unit[A](a: => A): F[A] } trait Monad[F[_]] { def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] def unit[A](a: => A): F[A] } flatmap + unit map + join + unit Kleisli composition + unit bind + return fmap + join + return Kleisli composition + return Defining a Monad in terms of map (fmap), join and unit (return)
  • 9.
    trait Monad[F[_]] { defflatMap[A,B](ma: F[A])(f: A => F[B]): F[B] def unit[A](a: => A): F[A] def join[A](mma: F[F[A]]): F[A] = flatMap(mma)(ma => ma) def map[A,B](m: F[A])(f: A => B): F[B] = flatMap(m)(a => unit(f(a))) def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] = a => flatMap(f(a))(g) } trait Functor[F[_]] { def map[A,B](m: F[A])(f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { def join[A](mma: F[F[A]]): F[A] def unit[A](a: => A): F[A] def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B] = join(map(ma)(f)) def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] = a => flatMap(f(a))(g) } trait Monad[F[_]] { def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] def unit[A](a: => A): F[A] def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B] = compose((_:Unit) => ma, f)(()) def map[A,B](m: F[A])(f: A => B): F[B] = flatMap(m)(a => unit(f(a))) } flatmap + unit map + join + unit Kleisli composition + unit Functional Programming in Scala defining join, map and compose in terms of flatMap and unit defining flatMap and map in terms of compose and unit defining flatMap and compose in terms of join and map Using primitive monad combinators to define the other key combinators (by Paul Chiusano and Runar Bjarnason) @pchiusano @runarorama
  • 10.
    Monads in CategoryTheory In Category Theory, a Monad is a functor equipped with a pair of natural transformations satisfying the laws of associativity and identity. What does this mean? If we restrict ourselves to the category of Scala types (with Scala types as the objects and functions as the arrows), we can state this in Scala terms. A Functor is just a type constructor for which map can be implemented: trait Functor[F[_]] { def map[A,B](fa: F[A])(f: A => B): F[B] } A natural transformation from a functor F to a functor G is just a polymorphic function: trait Transform[F[_], G[_]] { def apply[A](fa: F[A]): G[A] } The natural transformations that form a monad for F are unit and join: type Id[A] = A def unit[F](implicit F: Monad[F]) = new Transform[Id, F] { def apply(a: A): F[A] = F.unit(a) } def join[F](implicit F: Monad[F]) = new Transform[({type f[x] = F[F[x]]})#f, F] { def apply(ffa: F[F[A]]): F[A] = F.join(ffa) } In Category Theory a Monad is a functor equipped with a pair of natural transformations satisfying the laws of associativity and identity (by Runar Bjarnason) @runarorama
  • 11.
    All three definitions[of Monad] are used in category theory, but really, everybody uses the one with join, except that they don’t call it join and they don’t call it return. And they don’t call the functor M, they call it T. So the translation is m goes to T, join goes to µ and return goes to η. They use Greek letters here and it makes sesne because they use Greek letters for natural transformations and you will see that these (join and return) are natural transformations. So now I’ll switch notation to T, µ and η. We already talked about return at some point, when I talked about natural transformations, I said that return really is a polymorphic function that goes from a to ma, in the old notation, but really, since it is a polymorphic function, it is really a natural transformation a --> ma where a is the identity functor acting on a (Ida). So it is really a natural transformation between two functors, and Ida is a component of the natural transformation, for some particular a. So return is a component of the natural transformation from the identity functor to m, and since we don‘t want to use m here, I am going to use T. So we‘ll say that η (unit/return) is a natural transformation from the identity functor to T. And it means the same thing, except that in Haskell we use it in components, every natural transformation has components, so for a particular a, Ida acting on a gives you a, T acting on a gives you ma. Now what is µ?, µ (join) is also a natural transformation. Remember, join was going from m(m a) to ma. What is m(m a)? It means we take this functor, we act on a, and then we apply it to the result. So this is double application of the functor. It is just composition of the functor with itself. So this in mathematical notation would be T ∘ T --> T. Double application ot T, in components, it will give you m(m a), double application of a functor in components is m(m a). Single application of the functor is m a. It is a natural transformation. So in category theory we have to say it is a natural transformation. In Haskell we didn‘t say it is natural transformation, we didn‘t mention the naturality condition for return. Why? Because of polymorphism, because of ‘theorems for free‘, it is automatically a natural transformation, the naturality condition is automatic. But in category theory we have to say it is a natural transformation. So T ∘ T is usually written simply as T2. T2 is just compositon of T with itself. That‘s shorthand notation. So, a monad is a functor T and two natural transformations. Plus some laws, otherwise if we try to make a Kleisli category on top of this we wouldn‘t get associativity and identity laws. Bartosz Milewski defines a monad as a functor and two natural transformations, plus associativity and identity laws Category Theory 10.1: Monads @BartoszMilewski
  • 12.
    A B f FA FB F(f) GAGB G(f) F G C1 C1 and C2 are categories and ∘ denotes their composition operations. F and G are functors from C1 to C2 which map each C1 object to a C2 object and map each C1 arrow to a C2 arrow A natural transformation 𝜏 from F to G (either both covariant of both contravariant) is a family of arrows 𝜏 𝑋: FX → GX of C2 indexed by the object X of C1 such that for each arrow f: A → B of C1, the appropriate square in C2 commutes (depending on the variance) Natural Transformation 𝜏A 𝜏B G(f)∘ 𝜏A 𝜏B∘F(f) C2 𝜏 F G natural transformation 𝜏 from F to G 𝜏A FA GA GB 𝜏B GZ 𝜏Z …… … FA FB F(f) GA GB G(f) 𝜏A 𝜏B F(f)∘ 𝜏A 𝜏B∘G(f) FB FZ covariant contravariant the square commutes G(f)∘ 𝜏A= 𝜏B∘F(f) 𝜏 F G Naturality Condition Let’s recap on Natural Transformations (3 slides) https://www.slideshare.net/pjschwarz/natural-transformations @philip_schwarz
  • 13.
    A B f F[A] f↑F f↑G 𝜏A 𝜏B f↑G∘𝜏A 𝜏B∘f↑F 𝜏 F G natural transformation 𝜏 from F to G 𝜏A F[A] G[A] F[B] 𝜏B F[Z] 𝜏Z …… … F[A] F[B] f↑F G[A] G[B] f↑G 𝜏A 𝜏B F[B] G[A] G[B] G[B] G[Z] F(f)∘ 𝜏A 𝜏B∘G(f) F G C1 = C2 = Scala types and functions • objects: types • arrows: functions • composition operation: compose function, denoted here by ∘ • identity arrows: identity function T => T Functor F from C1 to C2 consisting of • type constructor F that maps type A to F[A] • a map function from function f:A=>B to function f↑F :F[A] => F[B] Functor G from C1 to C2 consisting of • type constructor G that maps type A to G[A] • a map function from function f:A=>B to function f↑G :G[A] => G[B] F[A] is type A lifted into context F f↑F is function f lifted into context F Generic Scala Example: Natural Transformation between two Functors from the category of ‘Scala types and functions’ to itself the square commutes f↑G ∘ 𝜏A = 𝜏B ∘ f↑F mapG f ∘ 𝜏A = 𝜏B ∘ mapF f 𝜏 F G covariant contravariant map lifts f into F f↑F is map f C1 = C2 = types and functions Naturality Condition https://www.slideshare.net/pjschwarz/natural-transformations @philip_schwarz
  • 14.
    String length List[String] List[Int] length↑List Option[String] Option[Int] Concrete Scala Example: safeHead- natural transformation 𝜏 from List functor to Option functor safeHead[String] = 𝜏StringInt length↑Option 𝜏Int = safeHead[Int] safeHead ∘length↑Option Option 𝜏 List Option natural transformation 𝜏 from List to Option 𝜏String List[String] Option[String] List[Int] 𝜏Int List[Char] 𝜏Char …… … Option[Int] Option[Char] length↑List ∘ safeHead covariant val length: String => Int = s => s.length // a natural transformation def safeHead[A]: List[A] => Option[A] = { case head::_ => Some(head) case Nil => None } the square commutes length↑List ∘ safeHead = safeHead ∘ length↑Option (mapList length) ∘ safeHead = safeHead ∘ (mapOption length) 𝜏 List Option F[A] is type A lifted into context F f↑F is function f lifted into context F map lifts f into F f↑F is map f C1 = C2 = types and functions List Naturality Condition https://www.slideshare.net/pjschwarz/natural-transformations @philip_schwarz
  • 15.
    FA FB A B FFAFFB FA FB GA GB FA FB A join join unit 𝜏A f f↑F (f↑F)↑F f↑F f↑G (f↑F)↑F ∘ unit unit ∘f↑F 𝜏B trait Functor[F[_]] { def map[A, B](f: A => B): F[A] => F[B] } trait Monad[F[_]] extends Functor[F] { def join[A](mma: F[F[A]]): F[A] def unit[A](a: => A): F[A] def flatMap[A,B](f: A => F[B]): F[A] => F[B] = (ma:F[A]) => join(map(f)(ma)) def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] = a => flatMap(g)(f(a)) } unit unit unit gg↑F f↑F ∘ unit unit ∘ f f↑F ∘ join join ∘(f↑F)↑F f↑G∘ 𝜏A 𝜏B ∘ f↑F flatMap g = join ∘ (map g) = join ∘ g↑F flatMap g map g FB A FC B g h g compose h calls g with A, then flatMaps h over result FB (to apply h to its contents), returning result FC. f pure function g,h effectful (kleisli) functions flatMap >>= compose <=< map <$> natural transformations: 𝜏 unit aka pure aka η join aka flatten aka μ map lifts f into F f↑F is map f Note: signatures of map and flatMap have here been rearranged (by swapping parameters around and uncurrying): they now return a function Monadic Combinators in Action primitive: unit and join – natural transformations derived: flatMap and compose (Kleisli composition) Creates a function which flatMap maps given function over its argument and joins the result compose calls 1st given function with its argument and flatMaps 2nd given function over the result maps g onto FA, then flattens resulting FFB, returning FA FC (flatMap h) ∘ g flatMap h = (flatMap h) ∘ gg compose h
  • 16.
    List[String] List[int] String Int List[List[String]]List[List[Int]] List[String] List[int] Option[String] Option[Int] List[String] List[Int] String join join unit safeHeadList length length↑L (length↑L)↑L length↑L length↑O (length↑L)↑L ∘ unit unit ∘length↑L unit unit unit asciiascii ↑L length↑L ∘ unit unit ∘ length length↑L ∘ join join ∘(length↑L)↑L length↑O∘ safeHeadList safeHeadOption ∘ length↑L flatMap ascii = join ∘ (map ascii) = join ∘ ascii↑L flatMap ascii map ascii calls ascii with a String, then flatMaps chars onto result List[Int] (producing a Char for each Int), returning result List[Char] natural transformations: safeHead unit aka pure aka η join aka flatten aka μ map lifts f into F f↑L is map f for F=List f↑O is map f for F=Option Concrete Example of Monadic Combinators in Action primitive: unit and join – natural transformations derived: flatMap and compose (Kleisli composition) Creates a function which flatMap maps given function over its argument and joins the result compose calls 1st given function with its argument and flatMaps 2nd given function over the result maps ascii onto List[String], then flattens resulting List[List[Int]], returning List[Int] safeHeadOption // a natural transformation def safeHead[A]: List[A] => Option[A] = { case head::_ => Some(head) case Nil => None } // an effectful (Kleisli) function - def ascii(s:String): List[Int] = if(s.isEmpty) Nil else s.head.toInt :: ascii(s.tail) // another effectful function - def chars(n:Int): List[Char] = n.toString.toList // a pure function val length: String => Int = s => s.length List[Int] String Int ascii chars ascii compose chars List[Char] (flatMap chars) ∘ ascii flatMap chars = (flatMap chars) ∘ asciiascii compose chars ascii("Abc") == List(65,98,99) chars(65) == List('6','5') List[Char] ascii("Abc") == List(65,98,99) chars(65) == List('6','5') (ascii compose chars)("Abc") = List('6','5','9','8','9','9')
  • 17.
    def safeHead[A]: List[A]=> Option[A] = { case head::_ => Some(head) case Nil => None } def ascii(s:String):List[Int] = if(s.isEmpty) Nil else s.head.toInt :: ascii(s.tail) def chars(n:Int):List[Char] = n.toString.toList assert(ascii("Abc") == List(65,98,99)) assert(chars(65) == List('6','5')) val listM = new Monad[List] { def map[A, B](f: A => B): List[A] => List[B] = { case head :: tail => f(head) :: map(f)(tail) case Nil => Nil } def unit[A](a: => A): List[A] = List(a) def join[A](mma: List[List[A]]): List[A] = mma match { case head::tail => head ::: join(tail) case Nil => Nil } } val optionM = new Monad[Option] { def map[A,B](f: A => B): Option[A] => Option[B] = { case Some(a) => Some(f(a)) case None => None } def unit[A](a: => A): Option[A] = Some(a) def join[A](mma: Option[Option[A]]): Option[A] = mma match { case Some(a) => a case None => None } } trait Functor[F[_]] { def map[A, B](f: A => B): F[A] => F[B] } trait Monad[F[_]] extends Functor[F] { def join[A](mma: F[F[A]]): F[A] def unit[A](a: => A): F[A] def flatMap[A,B](f: A => F[B]): F[A] => F[B] = (ma:F[A]) => join(map(f)(ma)) def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] = a => flatMap(g)(f(a)) } val length: String => Int = s => s.length val lengthLiftedOnce: List[String] => List[Int] = (listM map length) val lengthLiftedTwice: List[List[String]] => List[List[Int]] = listM map lengthLiftedOnce assert(length("abcd") == 4) assert(lengthLiftedOnce(List("abcd","efg","hi")) == List(4,3,2)) assert(lengthLiftedTwice(List(List("abcd","efg","hi"),List("jkl","mo","p"))) == List(List(4,3,2),List(3,2,1)))
  • 18.
    val computeAndThenTransform: List[List[String]]=> List[Int] = (listM.join[Int](_)) compose lengthLiftedTwice val transformAndThenCompute: List[List[String]] => List[Int] = lengthLiftedOnce compose listM.join assert(computeAndThenTransform(List(List("abcd","efg","hi"),List("jkl","mo","p"))) == transformAndThenCompute(List(List("abcd","efg","hi"),List("jkl","mo","p")))) assert(computeAndThenTransform(List(List("abcd","efg","hi"),List("jkl","mo","p"))) == List(4,3,2,3,2,1)) assert(transformAndThenCompute(List(List("abcd","efg","hi"),List("jkl","mo","p"))) == List(4,3,2,3,2,1)) val computeAndThenTransform: List[String] => List[List[Int]] = ((x:List[Int]) => listM.unit(x)) compose lengthLiftedOnce val transformAndThenCompute: List[String] => List[List[Int]] = lengthLiftedTwice compose ((x:List[String]) => listM.unit(x)) assert(computeAndThenTransform(List("abcd","efg","hi")) == transformAndThenCompute(List("abcd","efg","hi"))) assert(computeAndThenTransform(List("abcd","efg","hi")) == List(List(4,3,2))) assert(transformAndThenCompute(List("abcd","efg","hi")) == List(List(4,3,2))) val computeAndThenTransform: String => List[Int] = ((x:Int) => listM.unit(x)) compose length val transformAndThenCompute: String => List[Int] = lengthLiftedOnce compose ((x:String) => listM.unit(x)) assert(computeAndThenTransform("abcd") == transformAndThenCompute("abcd")) assert(computeAndThenTransform("abcd") == List(4)) assert(transformAndThenCompute("abcd") == List(4)) List[String] List[int] String Int List[List[String]] List[List[Int]] List[String] List[int] join join unit length length↑L (length↑L)↑L (length↑L)↑L ∘ unit unit ∘length↑L unit unit unit length↑L ∘ unit unit ∘ length length↑L ∘ join join ∘(length↑L)↑L val computeAndThenTransform: List[String] => Option[Int] = safeHead compose (listM map length) val transformAndThenCompute: List[String] => Option[Int] = (optionM map length) compose safeHead assert(computeAndThenTransform(List("abc", "d", "ef")) == transformAndThenCompute(List("abc", "d", "ef"))) assert(computeAndThenTransform(List("abc", "d", "ef")) == Some(3)) assert(transformAndThenCompute(List("abc", "d", "ef")) == Some(3)) assert(computeAndThenTransform(List()) == transformAndThenCompute(List())) assert(computeAndThenTransform(List()) == None) assert(transformAndThenCompute(List()) == None) Option[String] Option[Int] safeHeadList length↑O length↑O∘ safeHeadList safeHeadOption ∘ length↑L safeHeadOption
  • 19.
    List[List[String]] List[List[Int]] List[String] List[int] List[String]List[Int] String join join (length↑L)↑L length↑L asciiascii ↑L length↑L ∘ join join ∘(length↑L)↑L flatMap ascii = join ∘ (map ascii) = join ∘ ascii↑L flatMap ascii map ascii maps ascii onto List[String], then flattens resulting List[List[Int]], returning List[Int] def ascii(s:String):List[Int] = if(s.isEmpty) Nil else s.head.toInt :: ascii(s.tail) assert(ascii("Abc") == List(65,98,99)) val mappedAscii: List[String] => List[List[Int]] = listM map ascii val flatMappedAscii: List[String] => List[Int] = listM flatMap ascii val compositionOfMappedAsciiAndJoin = (listM.join[Int](_)) compose mappedAscii assert( compositionOfMappedAsciiAndJoin(List("abcd","efg","hi")) == flatMappedAscii(List("abcd","efg","hi")) ) assert( compositionOfMappedAsciiAndJoin(List("abcd","efg","hi")) == List(97, 98, 99, 100,101,102,103,104,105)) assert( flatMappedAscii(List("abcd","efg","hi")) == List(97, 98, 99, 100,101,102,103,104,105))
  • 20.
    def ascii(s:String):List[Int] =if(s.isEmpty) Nil else s.head.toInt :: ascii(s.tail) def chars(n:Int):List[Char] = n.toString.toList assert(chars(65) == List('6','5')) assert(ascii("Abc") == List(65,98,99)) def flatMappedChars = listM flatMap chars def kleisliCompositionOfAsciiAndChars = listM.compose(ascii, chars) assert( kleisliCompositionOfAsciiAndChars("Abc") == (flatMappedChars compose ascii)("Abc")) assert( kleisliCompositionOfAsciiAndChars("Abc") == List('6','5','9','8','9','9')) assert( (flatMappedChars compose ascii)("Abc") == List('6','5','9','8','9','9')) calls ascii with a String, then flatMaps chars onto result List[Int] (producing a Char for each Int), returning result List[Char] List[Int] String Int ascii chars ascii compose chars List[Char] (flatMap chars) ∘ ascii flatMap chars = (flatMap chars) ∘ asciiascii compose chars List[Char]