SlideShare a Scribd company logo
Sequence	and	Traverse
Part	1
@philip_schwarzslides	by
Paul	ChiusanoRunar	Bjarnason
@pchiusano@runarorama
learn	about	the	sequence	and	traverse	functions	
through	the	work	of
Functional Programming in Scala
(by	Paul	Chiusano	and	Runar	Bjarnason)
There turns out to be a startling number of operations
that can be defined in the most general possible way in
terms of sequence and/or traverse
There turns out to be a startling number of operations
that can be defined in the most general possible way in
terms of sequence and/or traverse
@pchiusano@runarorama
One of my favourite topics for
motivating usage of something like the
Cats library is this small thing called
Traverse.
I’d like to introduce you to this library called Cats, and
especially there, we are going to talk about this thing
called Traverse, which is, in my opinion, one of the
greatest productivity boosts I have ever had in my
whole programming career.
Luka Jacobowitz
@LukaJacobowitz
Oh, all the things you'll traverse
Sequence and Traverse - Part 1
Combines a list of Options into one Option containing a list of all the Some values in the original
list. If the original list contains None even once, the result of the function is None; otherwise the
result is Some with a list of all the values.
def sequence[A](a: List[Option[A]]): Option[List[A]]
Introducing	the	sequence	function
assert( sequence(Nil) == Some(List()) )
assert( sequence(List()) == Some(List()) )
if	the	list	is	empty	then	the	result	is	Some empty	list
assert( sequence(List(Some(1))) == Some(List(1)) )
assert( sequence(List(Some(1),Some(2))) == Some(List(1, 2)) )
assert( sequence(List(Some(1),Some(2),Some(3))) == Some(List(1, 2, 3)) )
if	the	list	contains all	Some values	then	the	result	is	Some list
if	the	list	contains	any	None value	then	the	result	is	None
assert( sequence(List(None)) == None )
assert( sequence(List(Some(1),None,Some(3))) == None )
assert( sequence(List(None,None,None)) == None )
Functional Programming in Scala
(by	Paul	Chiusano	and	Runar	Bjarnason)
@pchiusano @runarorama
Here’s	an	explicit	recursive	version:
def sequence[A](a: List[Option[A]]): Option[List[A]] = a match {
case Nil => Some(Nil)
case h :: t => h flatMap (hh => sequence(t) map (hh :: _))
}
It	can	also	be	implemented	using foldRight and	map2
def sequence[A](a: List[Option[A]]): Option[List[A]] =
a.foldRight[Option[List[A]]](Some(Nil))((h,t) => map2(h,t)(_ :: _))
Implementing	the	sequence	function
map2 being	defined	using	flatMap and	map	(for	example)
def map2[A,B,C](oa: Option[A], ob: Option[B])(f: (A, B) => C): Option[C] =
oa flatMap (a => ob map (b => f(a, b)))
def map2[A,B,C](oa: Option[A], ob: Option[B])(f: (A, B) => C): Option[C] =
for {
a <- oa
b <- ob
} yield f(a, b)
or	what	is	equivalent,	using	a	for	comprehension
assert( map2(Some(3),Some(5))(_ + _) == Some(8) )
assert( map2(Some(3),Option.empty[Int])(_ + _) == None )
assert( map2(Option.empty[Int],Some(5))(_ + _) == None )
by	Runar	Bjarnason
@runarorama
A	simple	example	of	using	the	sequence	function
Sometimes we’ll want to map over a list using a function that might fail, returning None if
applying it to any element of the list returns None.
For example, what if we have a whole list of String values that we wish to parse to
Option[Int]? In that case, we can simply sequence the results of the map.
import scala.util.Try
def parseIntegers(a: List[String]): Option[List[Int]] =
sequence(a map (i => Try(i.toInt).toOption))
assert( parseIntegers(List("1", "2", "3")) == Some(List(1, 2, 3) )
assert( parseIntegers(List("1", "x", "3")) == None )
assert( parseIntegers(List("1", "x", "1.2")) == None )
Functional Programming in Scala
(by	Paul	Chiusano	and	Runar	Bjarnason)
@pchiusano @runarorama
def parseIntegers(a: List[String]): Option[List[Int]] =
sequence(a map (i => Try(i.toInt).toOption))
Wanting to sequence the results of a map this way is a common enough occurrence to warrant a
new generic function, traverse
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = ???
Introducing	the	traverse	function
But this is inefficient, since it traverses the list twice, first to convert each String to an Option[Int], and a second pass
to combine these Option[Int] values into an Option[List[Int]].
It	is	straightforward	to	implement	traverse	using	map and	sequence
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
sequence(a map f)
Functional Programming in Scala
(by	Paul	Chiusano	and	Runar	Bjarnason)
@pchiusano @runarorama
def parseIntegers(a: List[String]): Option[List[Int]] =
traverse(a)(i => Try(i.toInt).toOption)
Better	implementations	of	the	traverse	function
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
a match {
case Nil => Some(Nil)
case h::t => map2(f(h), traverse(t)(f))(_ :: _)
}
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
a.foldRight[Option[List[B]]](Some(Nil))((h,t) => map2(f(h),t)(_ :: _))
Here’s	an	explicit	recursive	implementation	using	map2:
And	here	is	a	non-recursive	implementation	using	foldRight and	map2
def sequence[A](a: List[Option[A]]): Option[List[A]] =
a.foldRight[Option[List[A]]](Some(Nil))((h,t) => map2(h,t)(_ :: _))
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
a.foldRight[Option[List[B]]](Some(Nil))((h,t) => map2(f(h),t)(_ :: _))
Just	in	case	it	helps,	let’s	compare	the	implementation	of traverse with	that	of	sequence
by	Runar	Bjarnason
@runarorama
The	close	relationship	between	sequence and	traverse
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
sequence(a map f)
def sequence[A](a: List[Option[A]]): Option[List[A]] =
traverse(a)(x => x)
Just	like	it	is	possible	to	define	traverse in	terms	of	sequence
It	is	possible	to	define	sequence in	terms	of	traverse
While	this	implementation	of	
traverse	is	inefficient,	it	is	still	
useful	to	think	of	traverse as	
first	map and	then	sequence.
Actually, it turns out that there is a similar
relationship between monadic combinators
flatMap and flatten (see next two slides).
@philip_schwarz
trait Monad[F[_]] {
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
def unit[A](a: => A): F[A]
}
flatmap +	unit
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]
}
map	+	join +	unit
The	join function	takes	an	F[F[A]]	and	returns	an	F[A]
def join[A](mma: F[F[A]]): F[A]
What	it	does	is	“remove	a	layer”	of	F.	
The	flatMap function	takes	an	F[A]	and	a	function	from	A	to	F[B]	and	
returns	an	F[B]
def flatMap[A,B](ma: F[A])(f: A ⇒ F[B]): F[B]
What	it	does	is	apply	to	each	A	element	of	ma	a	function	f	producing	an	
F[B],	but	instead	of	returning	the	resulting	F[F[B]],	it	flattens	it	and	
returns	an	F[B].	
Recall	two	of	the	three	minimal	sets	of	combinators that	can	be	used	to	define	a	monad.	
One	set	includes	flatMap and	the	other	includes	join (in	Scala this	is	also	known	as	flatten)
@philip_schwarz
It	turns	out	that	flatMap can	be	defined	in	terms	of	map and	flatten
def flatMap[A,B](ma: F[A])(f: A ⇒ F[B]): F[B] = flatten(ma map f)
and		flatten can	be	defined	in	terms	of	flatMap
def flatten[A](mma: F[F[A]]): F[A] = flatMap(mma)(x ⇒ x)
So	flatMapping	a	function	is	just	mapping	the	function	first	and	then	flattening	the	result
and		flattening	is	just	flatMapping	the	identity	function x => x
Now	recall	that		traverse can	be	defined	in	terms	of	map and	sequence:
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = sequence(a map f)
and	sequence can	be	defined	in	terms	of	traverse
def sequence[A](a: List[Option[A]]): Option[List[A]] = traverse(a)(x ⇒ x)
flatMapping is	mapping	and	then	flattening	- flattening is	just	flatMapping	identity
traversing	is	mapping	and	then	sequencing	- sequencing	is	just	traversing	with	identity	
So	here	is	the	
similarity
@philip_schwarz
@philip_schwarz
But	there	is	another	similarity:	sequence and	join are	both	natural	transformations.
Before	we	can	look	at	why	that	is	the	case,	let’s	quickly	recap	what	a	natural	
transformation is.
See	the	following	for	a	less	hurried	introduction	to	natural	transformations:	
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↑List
Option
𝜏 =	safeHead
List Option
natural	transformation	𝜏	from	List to	Option
𝜏String
List[String] Option[String]
List[Int]
𝜏Int
List[Char]
𝜏Char
…… …
Option[Int]
Option[Char]
length↑Option	∘	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
}
𝜏
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 =
List
Naturality	
Condition
the square commutes
safeHead ∘ length↑List = length↑Option ∘	safeHead
safeHead ∘ (mapList length)	 = (mapOption length) ∘	safeHead
C1 C2
Category	of
types	and	functions
the square commutes
https://www.slideshare.net/pjschwarz/natural-transformations
@philip_schwarz
trait Functor[F[_]] {
def map[A, B](f: A => B): F[A] => F[B]
}
val listF = new Functor[List] {
def map[A,B](f: A => B): List[A] => List[B] = {
case head::tail => f(head)::map(f)(tail)
case Nil => Nil
}
}
val length: String => Int = s => s.length
def safeHead[A]: List[A] => Option[A] = {
case head::_ => Some(head)
case Nil => None
}
val mapAndThenTransform: List[String] => Option[Int] = safeHead compose (listF map length)
val transformAndThenMap: List[String] => Option[Int] = (optionF map length) compose safeHead
assert(mapAndThenTransform(List("abc", "d", "ef")) == transformAndThenMap(List("abc", "d", "ef")))
assert(mapAndThenTransform(List("abc", "d", "ef")) == Some(3))
assert(transformAndThenMap(List("abc", "d", "ef")) == Some(3))
assert(mapAndThenTransform(List()) == transformAndThenMap(List()))
assert(mapAndThenTransform(List()) == None)
assert(transformAndThenMap(List()) == None)
val optionF = new Functor[Option] {
def map[A,B](f: A => B): Option[A] => Option[B] = {
case Some(a) => Some(f(a))
case None => None
}
}
the square commutes
safeHead ∘ length↑List = length↑Option ∘	safeHead
safeHead ∘ (mapList length)	 = (mapOption length) ∘	safeHead
𝜏
List Option
mapF lifts	f	into	F
so	f↑F		is	map f
Concrete	Scala	Example:	safeHead - natural	transformation	𝜏	from	List functor	to	Option functor
Naturality	
Condition
https://www.slideshare.net/pjschwarz/natural-transformations
@philip_schwarz
Having	recapped	what	a	natural	transformation is,	
let’s	see	why	join is	a	natural	transformation.
@philip_schwarz
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
monadic combinators unit and
join are natural transformations
So	is	sequence a	natural	transformation,	just		like	safeHead,	unit and	join?
@philip_schwarz
safeHead: List[A] ⇒ Option[A]
unit: A ⇒ List[A]
join: List[List[A]] ⇒ List[A]
sequence: List[Option[A]] ⇒ Option[List[A]]
Hmm,	sequence differs	from	the	other	functions	in	
that	its	input	and	output	types	are	nested	functors.
But	wait,	the	input	of	join is	also	a	nested	functor.
Better	check	with	the	experts…
Asking the experts – exhibit number 1: for sequence, just like for safeHead, first transforming and then mapping is the
same as first mapping and then transforming
It	seems	to	me	that	sequence is	a	natural	transformation,	like	safeHead,	but	in	some	higher-order	sense.	Is	that	right?	
def safeHead[A]: List[A] => Option[A]
def sequence[A](a: List[Option[A]]): Option[List[A]]
• natural	transformation	safeHead maps	the	List	Functor	to	the	Option	Functor
• I	can	either	first apply	safeHead to	a	list	and	then	map	the	length	function	over	the	result	
• Or	I	can	first map	the	length	function	over	the	list	and	then	apply	safeHead to	the	result
• The	overall	result	is	the	same
• In	the	first	case,	map	is	used	to	lift	the	length	function	into	the	List	Functor
• In	the	second	case,	map	is	used	to	lift	the	length	function	into	the	Option	Functor
Is	it	correct	to	consider	sequence	to	be	a	natural	transformation? I	ask	because	there	seems	to	be	something	higher-order	about	sequence
compared	to	safeHead
• natural	transformation	sequence maps	a	List	Functor	of	an	Option	Functor	to	an	Option	Functor	of	a	List	Functor
• I	can	either	first apply	sequence to	a	list	of	options	and	then	map	a	function	that	maps	the	length	function
• Or	I	can	first map	over	the	list	a	function	that	maps	the	length	function,	and	then	sequence the	result
• The	overall	result	is	the	same
• In	the	first	case,	we	first	use	map	to	lift	the	length	function	into	the	List	Functor	and	then	again	to	lift	the	resulting	function	into	the	Option	
Functor,	
• In	the	second	case,	we	first	use	map	to	lift	the	length	function	into	the	Option	Functor	and	then	again	to	lift	the	resulting function	into	the	List	
Functor
It	seems	that	for	a	natural	transformation	that	rearranges	N	layers	of	Functors	we	call	map	on	each	of	those	layers	before	we apply	a	function.		
Asking	the	experts – exhibit	number	2:	why	sequence seems	to	be	a	natural	transformation,	albeit	a	more	complex	one
Sequence and Traverse - Part 1
safeHead: List[A] ⇒ Option[A]
unit: A ⇒ List[A]
join: List[List[A]] ⇒ List[A]
sequence: List[Option[A]] ⇒ Option[List[A]]
So	yes,	sequence,	like	safeHead,	unit and	join,	is	a	natural	transformation.	
They	are	all	polymorphic	functions	from	one	functor to	another.	
@philip_schwarz
Let’s	illustrate	this	further	in	the	next	slide	using	diagrams	and	some	code.
List[String] List[int]
String Int
List[List[String]] List[List[Int]]
List[String] List[int]
Option[String] Option[Int]
join join
unit
safeHeadList
length
length↑L
(length↑L)↑L
length↑L
length↑O
(length↑L)↑L ∘ unit
unit ∘length↑L
unit
unit unit
length↑L ∘ unit
unit ∘ length
length↑L ∘ join
join ∘(length↑L)↑L
length↑O∘ safeHeadList
safeHeadOption ∘	length↑L
safeHeadOption
Option[List[String]]
List[Option[Int]]
sequence
(length↑O)↑L
(length↑L)↑O ∘ sequence
sequence ∘ (length ↑ O) ↑ L
sequence
List[Option[String]]
Option[List[Int]]
(length↑L)↑O
natural	transformations:	
safeHead	
unit	aka	pure aka	η
join	aka	flatten	aka	μ
sequence
map	lifts	f	into	F
f↑L		is	map	f	for	F=List
f↑O		is	map	f	for	F=Option
val expected = Some(2)
// first map and then transform
assert(safeHead(List("ab","abc","a").map(_.length)) == expected)
// first transform and then map
assert((safeHead(List("ab","abc","a")).map(_.length)) == expected)
val expected = List(2,3,1)
// first map and then transform
assert(List(List("ab","abc"),Nil,List("a")).map(_.map(_.length)).flatten == expected)
// first transform and then map
assert(List(List("ab","abc"),Nil,List("a")).flatten.map(_.length) == expected)
val expected = Some(List(2,3,1))
// first map and then transform
assert(sequence(List(Some("ab"),Some("abc"),Some("a")).map.(_.map(_.length))) == expected)
// first transform and then map
assert(sequence(List(Some("ab"),Some("abc"),Some("a"))).map(_.map(_.length)) == expected)
Combines a list of Options into one Option containing a list of all the Some values in the original list. If
the original list contains None even once, the result of the function is None ; otherwise the
result is Some with a list of all the values.
def sequence[A] (a: List[Option[ A]]): Option[ List[A]]
From	sequence	for	Option to	sequence for	Either
def sequence[E,A](a: List[Either[E,A]]): Either[E,List[A]]
Combines a list of Eithers into one Either containing a list of all the Right values in the original list.
If the original list contains Left even once, the result of the function is the first Left; otherwise the
result is Right with a list of all the values.
The	sequence	function	for	Either
assert( sequence(Nil) == Right(List()) )
assert( sequence(List()) == Right(List()) )
if	the	list	is	empty	then	the	result	is	Right of	empty	list
assert( sequence(List(Right(1))) == Right(List(1)) )
assert( sequence(List(Right(1),Right(2))) == Right(List(1, 2)) )
assert( sequence(List(Right(1),Right(2),Right(3))) == Right(List(1, 2, 3)) )
if	the	list	contains all	Right values	then	the	result	is	Right of	a	list
if	the	list	contains	any	Left value	then	the	result	is	the	first	 Left value
assert( sequence(List(Left(-1))) == Left(-1) )
assert( sequence(List(Right(1),Left(-2),Right(3))) == Left(-2) )
assert( sequence(List(Left(0),Left(-1),Left(-2))) == Left(0) )
def sequence[E,A](a: List[Either[E,A]]): Either[E,List[A]]
Combines a list of Eithers into one Either containing a list of all the Right values in the original
list. If the original list contains Left even once, the result of the function is the first Left; otherwise
the result is Right with a list of all the values.
explicit recursive
implementation
def sequence[A](a: List[Option[A]]): Option[List[A]] = a match {
case Nil => Some(Nil)
case h :: t => h flatMap (hh => sequence(t) map (hh :: _))
}
def sequence[E,A](a: List[Either[E,A]]): Either[E,List[A]] = a match {
case Nil => Right(Nil)
case h :: t => h flatMap (hh => sequence(t) map (hh :: _))
}
From	implementation	of	sequence	for	Option to	implementation	of	sequence for	Either
Implementation using
foldRight and map2
def sequence[A](a: List[Option[A]]): Option[List[A]] =
a.foldRight[Option[List[A]]](Some(Nil))((h,t) => map2(h,t)(_ :: _))
def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] =
a flatMap (aa => b map (bb => f(aa, ab)))
def sequence[A,E](a: List[Either[E,A]]): Either[E,List[A]] =
a.foldRight[Either[E,List[A]]](Right(Nil))((h,t) => map2(h,t)(_ :: _))
def map2[A,B,C,E](a: Either[E,A], b: Either[E,B])(f: (A, B) => C): Either[E,C] =
a flatMap (aa => b map (bb => f(aa, bb)))
explicit recursive
implementation
Implementation using
foldRight and map2
From	implementation	of	traverse	for	Option to	implementation	of	traverse	for	Either
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a match {
case Nil => Some(Nil)
case h::t => map2(f(h), traverse(t)(f))(_ :: _)
}
def traverse[A,B,E](a: List[A])(f: A => Either[E, B]): Either[E,List[B]] = a match {
case Nil => Right(Nil)
case h::t => map2(f(h),traverse(t)(f))(_ :: _)
}
def traverse[A,B](a: List[A])(f: A => Option[B]): Option[List[B]] =
a.foldRight[Option[List[B]]](Some(Nil))((h,t) => map2(f(h),t)(_ :: _))
def traverse[A,B,E](a: List[A])(f: A => Either[E,B]): Either[E,List[B]] =
a.foldRight[Either[E,List[B]]](Right(Nil))((h, t) => map2(f(h),(t))(_ :: _))
Simple	example	of	using	sequence	function,	revisited	for	Either
Sometimes we’ll want to map over a list using a function that might fail, returning Left[Throwable] if applying it to any element of the list returns
Left[Throwable].
For example, what if we have a whole list of String values that we wish to parse to Either[Throwable,Int]? In that case, we can simply sequence the
results of the map.
import scala.util.Try
def parseIntegers(a: List[String]): Either[Throwable,List[Int]] =
sequence(a map (i => Try(i.toInt).toEither))
assert( parseIntegers(List("1", "2", "3")) == Right(List(1, 2, 3) )
assert( parseIntegers(List("1", "x", "3")) == Left(java.lang.NumberFormatException: For input string: "x") )
assert( parseIntegers(List("1", "x", "1.2")) == Left(java.lang.NumberFormatException: For input string: "x") )
The	close	relationship	between	sequence and	traverse,	revisited	for	Either
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
sequence(a map f)
def sequence[A](a: List[Option[A]]): Option[List[A]] =
traverse(a)(x => x)
def traverse[A,B,E](a: List[A])(f: A => Either[E,B]): Either[E,List[B]] =
sequence(a map f)
def sequence[A,E](a: List[Either[E,A]]): Either[E,List[A]] =
traverse(a)(x => x)
defining traverse in
terms of map and
sequence
defining sequence in
terms of traverse and
identity
The sequence and traverse functions we have seen so far (and the map and map2 functions they depend on), are
specialised for a particular type constructor, e.g. Option or Either. Can they be generalised so that they work on
many more type constructors?
@philip_schwarz
def sequence[A](a: List[Option[A]]): Option[List[A]] =
a.foldRight[Option[List[A]]](Some(Nil))((h,t) => map2(h,t)(_ :: _))
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
a.foldRight[Option[List[B]]](Some(Nil))((h,t) => map2(f(h),t)(_ :: _))
def map2[A,B,C](oa: Option[A], ob: Option[B])(f: (A, B) => C): Option[C] =
oa flatMap (a => ob map (b => f(a, b)))
E.g. in the above example, the Option specific items that sequence, traverse and map2 depend on are Option’s
Some constructor, Option’s map function, and Option’s flatMap function. In the case of Either, the dependencies
are on Either’s Right constructor, Either’s map function and Either’s flatMap function.
Option and Either are monads and every monad has a unit function, a map function, and a flatMap function.
Since Some and Right are Option and Either’s unit functions and since map2 can be implemented using map and
flatMap, it follows that sequence and traverse can be implemented for every monad.
See the next slide for a reminder that every monad has unit, map and flatMap functions.
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
The three different ways of
defining a monad, and how
a monad always has the
following three functions:
unit, map, flatMap
def sequence[A](a: List[Option[A]]): Option[List[A]]
def sequence[A](a: List[F[A]]): F[List[A]]
def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]]
def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C]
def traverse[A,B](a: List[A])(f: A => F[B]): F[List[B]]
def map2[A,B,C](a: F[A], b: F[B])(f: (A, B) => C): F[C]
Generalising	the	signatures	of	map2,	sequence	and	traverse	so	they	work	on	a	monad	F
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B): F[B]
}
trait Monad[F[_]] extends Functor[F] {
def unit[A](a: => A): F[A]
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
def map[A,B](ma: F[A])(f: A => B): F[B] =
flatMap(ma)(a => unit(f(a)))
def map2[A,B,C](ma: F[A], mb: F[B])(f: (A, B) => C): F[C] =
flatMap(ma)(a => map(mb)(b => f(a, b)))
def sequence[A](lma: List[F[A]]): F[List[A]] =
lma.foldRight(unit(List[A]()))((ma, mla) => map2(ma, mla)(_ :: _))
def traverse[A,B](la: List[A])(f: A => F[B]): F[List[B]] =
la.foldRight(unit(List[B]()))((a, mlb) => map2(f(a), mlb)(_ :: _))
}
Functional Programming in Scala
How	a	monad can	define	sequence and	traverse
A	simple	example	of	using	the	traversable	function	of	the	Option	monad
val optionM = new Monad[Option] {
def unit[A](a: => A): Option[A] = Some(a)
def flatMap[A,B](ma: Option[A])(f: A => Option[B]): Option[B] = ma match {
case Some(a) => f(a)
case None => None
}
}
def parseIntsMaybe(a: List[String]): Option[List[Int]] =
optionM.traverse(a)(i => Try(i.toInt).toOption)
scala> parseIntsMaybe(List("1", "2", "3"))
res0: Option[List[Int]] = Some(List(1, 2, 3))
scala> parseIntsMaybe(List("1", "x", "3"))
res1: Option[List[Int]] = None
scala> parseIntsMaybe(List("1", "x", "y"))
res2: Option[List[Int]] = None
A	simple	example	of	using	the	traversable	function	of	the	Either monad
type Validated[A] = Either[Throwable,A]
val eitherM = new Monad[Validated] {
def unit[A](a: => A): Validated[A] = Right(a)
def flatMap[A,B](ma: Validated[A])(f: A => Validated[B]): Validated[B] = ma match {
case Left(l) => Left(l)
case Right(a) => f(a)
}
}
def parseIntsValidated(a: List[String]): Either[Throwable,List[Int]] =
eitherM.traverse(a)(i => Try(i.toInt).toEither)
scala> parseIntsValidated(List("1", "2", "3"))
res0: Either[Throwable,List[Int]] = Right(List(1, 2, 3))
scala> parseIntsValidated(List("1", "x", "3"))
res1: Either[Throwable,List[Int]] = Left(java.lang.NumberFormatException: For input string: "x")
scala> parseIntsValidated(List("1", "x", "1.2"))
res2: Either[Throwable,List[Int]] = Left(java.lang.NumberFormatException: For input string: "x")
But is it necessary to use a monad in order
to define generic sequence and traverse
methods? Find out in part 2.
@philip_schwarz

More Related Content

What's hot (20)

PPTX
ZIO: Powerful and Principled Functional Programming in Scala
Wiem Zine Elabidine
 
PDF
Izumi 1.0: Your Next Scala Stack
7mind
 
PDF
ZIO Queue
John De Goes
 
PDF
Preparing for Scala 3
Martin Odersky
 
PDF
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Jorge Vásquez
 
PDF
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Philip Schwarz
 
PDF
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
Philip Schwarz
 
PDF
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - Part ...
Philip Schwarz
 
PDF
Logstage - zero-cost-tructured-logging
7mind
 
PDF
non-strict functions, bottom and scala by-name parameters
Philip Schwarz
 
PDF
Peeking inside the engine of ZIO SQL.pdf
JaroslavRegec1
 
PPTX
Taking your side effects aside
💡 Tomasz Kogut
 
PDF
Applicative Functor
Philip Schwarz
 
PPT
Linked lists
SARITHA REDDY
 
PPTX
Deque and its applications
Jsaddam Hussain
 
PDF
Refactoring Functional Type Classes
John De Goes
 
PDF
Ad hoc Polymorphism using Type Classes and Cats
Philip Schwarz
 
PDF
Queues
Hareem Aslam
 
PPTX
Queue_Data_Structure.pptx
sandeep54552
 
PPT
Binary operator overloading
BalajiGovindan5
 
ZIO: Powerful and Principled Functional Programming in Scala
Wiem Zine Elabidine
 
Izumi 1.0: Your Next Scala Stack
7mind
 
ZIO Queue
John De Goes
 
Preparing for Scala 3
Martin Odersky
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Jorge Vásquez
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Philip Schwarz
 
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
Philip Schwarz
 
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - Part ...
Philip Schwarz
 
Logstage - zero-cost-tructured-logging
7mind
 
non-strict functions, bottom and scala by-name parameters
Philip Schwarz
 
Peeking inside the engine of ZIO SQL.pdf
JaroslavRegec1
 
Taking your side effects aside
💡 Tomasz Kogut
 
Applicative Functor
Philip Schwarz
 
Linked lists
SARITHA REDDY
 
Deque and its applications
Jsaddam Hussain
 
Refactoring Functional Type Classes
John De Goes
 
Ad hoc Polymorphism using Type Classes and Cats
Philip Schwarz
 
Queues
Hareem Aslam
 
Queue_Data_Structure.pptx
sandeep54552
 
Binary operator overloading
BalajiGovindan5
 

Similar to Sequence and Traverse - Part 1 (20)

PDF
Sequence and Traverse - Part 2
Philip Schwarz
 
PDF
Functor Composition
Philip Schwarz
 
PDF
Sequence and Traverse - Part 3
Philip Schwarz
 
PDF
Frp2016 3
Kirill Kozlov
 
PDF
Scala. Introduction to FP. Monads
Kirill Kozlov
 
PDF
Fp in scala part 1
Hang Zhao
 
PDF
Fp in scala with adts part 2
Hang Zhao
 
PDF
Fp in scala part 2
Hang Zhao
 
PDF
purrr.pdf
Mateus S. Xavier
 
PPT
Functional programming with_scala
Raymond Tay
 
PDF
Fp in scala with adts
Hang Zhao
 
PPTX
Monads and friends demystified
Alessandro Lacava
 
PPTX
Python for R users
Satyarth Praveen
 
PDF
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Philip Schwarz
 
PDF
Oh, All the things you'll traverse
Luka Jacobowitz
 
PDF
Monad presentation scala as a category
samthemonad
 
PDF
Scala collection methods flatMap and flatten are more powerful than monadic f...
Philip Schwarz
 
PDF
Why Haskell Matters
romanandreg
 
PDF
Modular Module Systems
league
 
PPT
Thesis
Drew Ferkin
 
Sequence and Traverse - Part 2
Philip Schwarz
 
Functor Composition
Philip Schwarz
 
Sequence and Traverse - Part 3
Philip Schwarz
 
Frp2016 3
Kirill Kozlov
 
Scala. Introduction to FP. Monads
Kirill Kozlov
 
Fp in scala part 1
Hang Zhao
 
Fp in scala with adts part 2
Hang Zhao
 
Fp in scala part 2
Hang Zhao
 
purrr.pdf
Mateus S. Xavier
 
Functional programming with_scala
Raymond Tay
 
Fp in scala with adts
Hang Zhao
 
Monads and friends demystified
Alessandro Lacava
 
Python for R users
Satyarth Praveen
 
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Philip Schwarz
 
Oh, All the things you'll traverse
Luka Jacobowitz
 
Monad presentation scala as a category
samthemonad
 
Scala collection methods flatMap and flatten are more powerful than monadic f...
Philip Schwarz
 
Why Haskell Matters
romanandreg
 
Modular Module Systems
league
 
Thesis
Drew Ferkin
 
Ad

More from Philip Schwarz (20)

PDF
Folding Cheat Sheet Series Titles - a series of 9 decks
Philip Schwarz
 
PDF
Folding Cheat Sheet # 9 - List Unfolding 𝑢𝑛𝑓𝑜𝑙𝑑 as the Computational Dual of ...
Philip Schwarz
 
PDF
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
Philip Schwarz
 
PDF
Drawing Heighway’s Dragon - Part 4 - Interactive and Animated Dragon Creation
Philip Schwarz
 
PDF
The Nature of Complexity in John Ousterhout’s Philosophy of Software Design
Philip Schwarz
 
PDF
Drawing Heighway’s Dragon - Part 3 - Simplification Through Separation of Con...
Philip Schwarz
 
PDF
The Open-Closed Principle - Part 2 - The Contemporary Version - An Introduction
Philip Schwarz
 
PDF
The Open-Closed Principle - Part 1 - The Original Version
Philip Schwarz
 
PDF
Drawing Heighway’s Dragon - Part II - Recursive Function Simplification - Fro...
Philip Schwarz
 
PDF
Drawing Heighway’s Dragon - Recursive Function Rewrite - From Imperative Styl...
Philip Schwarz
 
PDF
Fibonacci Function Gallery - Part 2 - One in a series
Philip Schwarz
 
PDF
Fibonacci Function Gallery - Part 1 (of a series) - with minor corrections
Philip Schwarz
 
PDF
Fibonacci Function Gallery - Part 1 (of a series)
Philip Schwarz
 
PDF
The Debt Metaphor - Ward Cunningham in his 2009 YouTube video
Philip Schwarz
 
PDF
Folding Cheat Sheet Series Titles (so far)
Philip Schwarz
 
PDF
From Subtype Polymorphism To Typeclass-based Ad hoc Polymorphism - An Example
Philip Schwarz
 
PDF
Folding Cheat Sheet #8 - eighth in a series
Philip Schwarz
 
PDF
Function Applicative for Great Good of Leap Year Function
Philip Schwarz
 
PDF
Folding Cheat Sheet #7 - seventh in a series
Philip Schwarz
 
PDF
Folding Cheat Sheet #6 - sixth in a series
Philip Schwarz
 
Folding Cheat Sheet Series Titles - a series of 9 decks
Philip Schwarz
 
Folding Cheat Sheet # 9 - List Unfolding 𝑢𝑛𝑓𝑜𝑙𝑑 as the Computational Dual of ...
Philip Schwarz
 
List Unfolding - 'unfold' as the Computational Dual of 'fold', and how 'unfol...
Philip Schwarz
 
Drawing Heighway’s Dragon - Part 4 - Interactive and Animated Dragon Creation
Philip Schwarz
 
The Nature of Complexity in John Ousterhout’s Philosophy of Software Design
Philip Schwarz
 
Drawing Heighway’s Dragon - Part 3 - Simplification Through Separation of Con...
Philip Schwarz
 
The Open-Closed Principle - Part 2 - The Contemporary Version - An Introduction
Philip Schwarz
 
The Open-Closed Principle - Part 1 - The Original Version
Philip Schwarz
 
Drawing Heighway’s Dragon - Part II - Recursive Function Simplification - Fro...
Philip Schwarz
 
Drawing Heighway’s Dragon - Recursive Function Rewrite - From Imperative Styl...
Philip Schwarz
 
Fibonacci Function Gallery - Part 2 - One in a series
Philip Schwarz
 
Fibonacci Function Gallery - Part 1 (of a series) - with minor corrections
Philip Schwarz
 
Fibonacci Function Gallery - Part 1 (of a series)
Philip Schwarz
 
The Debt Metaphor - Ward Cunningham in his 2009 YouTube video
Philip Schwarz
 
Folding Cheat Sheet Series Titles (so far)
Philip Schwarz
 
From Subtype Polymorphism To Typeclass-based Ad hoc Polymorphism - An Example
Philip Schwarz
 
Folding Cheat Sheet #8 - eighth in a series
Philip Schwarz
 
Function Applicative for Great Good of Leap Year Function
Philip Schwarz
 
Folding Cheat Sheet #7 - seventh in a series
Philip Schwarz
 
Folding Cheat Sheet #6 - sixth in a series
Philip Schwarz
 
Ad

Recently uploaded (20)

PPT
Activate_Methodology_Summary presentatio
annapureddyn
 
PDF
Why Are More Businesses Choosing Partners Over Freelancers for Salesforce.pdf
Cymetrix Software
 
PDF
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
PDF
On Software Engineers' Productivity - Beyond Misleading Metrics
Romén Rodríguez-Gil
 
PDF
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
PDF
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
PDF
Enhancing Security in VAST: Towards Static Vulnerability Scanning
ESUG
 
PPTX
Role Of Python In Programing Language.pptx
jaykoshti048
 
PDF
Infrastructure planning and resilience - Keith Hastings.pptx.pdf
Safe Software
 
PDF
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
PDF
Download iTop VPN Free 6.1.0.5882 Crack Full Activated Pre Latest 2025
imang66g
 
PDF
How to Download and Install ADT (ABAP Development Tools) for Eclipse IDE | SA...
SAP Vista, an A L T Z E N Company
 
PDF
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
PPTX
slidesgo-unlocking-the-code-the-dynamic-dance-of-variables-and-constants-2024...
kr2589474
 
PPTX
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 
PDF
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
PDF
SAP GUI Installation Guide for macOS (iOS) | Connect to SAP Systems on Mac
SAP Vista, an A L T Z E N Company
 
PDF
Adobe Illustrator Crack Full Download (Latest Version 2025) Pre-Activated
imang66g
 
PDF
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
PPTX
Presentation about variables and constant.pptx
kr2589474
 
Activate_Methodology_Summary presentatio
annapureddyn
 
Why Are More Businesses Choosing Partners Over Freelancers for Salesforce.pdf
Cymetrix Software
 
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
On Software Engineers' Productivity - Beyond Misleading Metrics
Romén Rodríguez-Gil
 
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
Enhancing Security in VAST: Towards Static Vulnerability Scanning
ESUG
 
Role Of Python In Programing Language.pptx
jaykoshti048
 
Infrastructure planning and resilience - Keith Hastings.pptx.pdf
Safe Software
 
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
Download iTop VPN Free 6.1.0.5882 Crack Full Activated Pre Latest 2025
imang66g
 
How to Download and Install ADT (ABAP Development Tools) for Eclipse IDE | SA...
SAP Vista, an A L T Z E N Company
 
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
slidesgo-unlocking-the-code-the-dynamic-dance-of-variables-and-constants-2024...
kr2589474
 
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
SAP GUI Installation Guide for macOS (iOS) | Connect to SAP Systems on Mac
SAP Vista, an A L T Z E N Company
 
Adobe Illustrator Crack Full Download (Latest Version 2025) Pre-Activated
imang66g
 
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
Presentation about variables and constant.pptx
kr2589474
 

Sequence and Traverse - Part 1

  • 2. Functional Programming in Scala (by Paul Chiusano and Runar Bjarnason) There turns out to be a startling number of operations that can be defined in the most general possible way in terms of sequence and/or traverse There turns out to be a startling number of operations that can be defined in the most general possible way in terms of sequence and/or traverse @pchiusano@runarorama
  • 3. One of my favourite topics for motivating usage of something like the Cats library is this small thing called Traverse. I’d like to introduce you to this library called Cats, and especially there, we are going to talk about this thing called Traverse, which is, in my opinion, one of the greatest productivity boosts I have ever had in my whole programming career. Luka Jacobowitz @LukaJacobowitz Oh, all the things you'll traverse
  • 5. Combines a list of Options into one Option containing a list of all the Some values in the original list. If the original list contains None even once, the result of the function is None; otherwise the result is Some with a list of all the values. def sequence[A](a: List[Option[A]]): Option[List[A]] Introducing the sequence function assert( sequence(Nil) == Some(List()) ) assert( sequence(List()) == Some(List()) ) if the list is empty then the result is Some empty list assert( sequence(List(Some(1))) == Some(List(1)) ) assert( sequence(List(Some(1),Some(2))) == Some(List(1, 2)) ) assert( sequence(List(Some(1),Some(2),Some(3))) == Some(List(1, 2, 3)) ) if the list contains all Some values then the result is Some list if the list contains any None value then the result is None assert( sequence(List(None)) == None ) assert( sequence(List(Some(1),None,Some(3))) == None ) assert( sequence(List(None,None,None)) == None ) Functional Programming in Scala (by Paul Chiusano and Runar Bjarnason) @pchiusano @runarorama
  • 6. Here’s an explicit recursive version: def sequence[A](a: List[Option[A]]): Option[List[A]] = a match { case Nil => Some(Nil) case h :: t => h flatMap (hh => sequence(t) map (hh :: _)) } It can also be implemented using foldRight and map2 def sequence[A](a: List[Option[A]]): Option[List[A]] = a.foldRight[Option[List[A]]](Some(Nil))((h,t) => map2(h,t)(_ :: _)) Implementing the sequence function map2 being defined using flatMap and map (for example) def map2[A,B,C](oa: Option[A], ob: Option[B])(f: (A, B) => C): Option[C] = oa flatMap (a => ob map (b => f(a, b))) def map2[A,B,C](oa: Option[A], ob: Option[B])(f: (A, B) => C): Option[C] = for { a <- oa b <- ob } yield f(a, b) or what is equivalent, using a for comprehension assert( map2(Some(3),Some(5))(_ + _) == Some(8) ) assert( map2(Some(3),Option.empty[Int])(_ + _) == None ) assert( map2(Option.empty[Int],Some(5))(_ + _) == None ) by Runar Bjarnason @runarorama
  • 7. A simple example of using the sequence function Sometimes we’ll want to map over a list using a function that might fail, returning None if applying it to any element of the list returns None. For example, what if we have a whole list of String values that we wish to parse to Option[Int]? In that case, we can simply sequence the results of the map. import scala.util.Try def parseIntegers(a: List[String]): Option[List[Int]] = sequence(a map (i => Try(i.toInt).toOption)) assert( parseIntegers(List("1", "2", "3")) == Some(List(1, 2, 3) ) assert( parseIntegers(List("1", "x", "3")) == None ) assert( parseIntegers(List("1", "x", "1.2")) == None ) Functional Programming in Scala (by Paul Chiusano and Runar Bjarnason) @pchiusano @runarorama
  • 8. def parseIntegers(a: List[String]): Option[List[Int]] = sequence(a map (i => Try(i.toInt).toOption)) Wanting to sequence the results of a map this way is a common enough occurrence to warrant a new generic function, traverse def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = ??? Introducing the traverse function But this is inefficient, since it traverses the list twice, first to convert each String to an Option[Int], and a second pass to combine these Option[Int] values into an Option[List[Int]]. It is straightforward to implement traverse using map and sequence def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = sequence(a map f) Functional Programming in Scala (by Paul Chiusano and Runar Bjarnason) @pchiusano @runarorama def parseIntegers(a: List[String]): Option[List[Int]] = traverse(a)(i => Try(i.toInt).toOption)
  • 9. Better implementations of the traverse function def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a match { case Nil => Some(Nil) case h::t => map2(f(h), traverse(t)(f))(_ :: _) } def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a.foldRight[Option[List[B]]](Some(Nil))((h,t) => map2(f(h),t)(_ :: _)) Here’s an explicit recursive implementation using map2: And here is a non-recursive implementation using foldRight and map2 def sequence[A](a: List[Option[A]]): Option[List[A]] = a.foldRight[Option[List[A]]](Some(Nil))((h,t) => map2(h,t)(_ :: _)) def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a.foldRight[Option[List[B]]](Some(Nil))((h,t) => map2(f(h),t)(_ :: _)) Just in case it helps, let’s compare the implementation of traverse with that of sequence by Runar Bjarnason @runarorama
  • 10. The close relationship between sequence and traverse def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = sequence(a map f) def sequence[A](a: List[Option[A]]): Option[List[A]] = traverse(a)(x => x) Just like it is possible to define traverse in terms of sequence It is possible to define sequence in terms of traverse While this implementation of traverse is inefficient, it is still useful to think of traverse as first map and then sequence. Actually, it turns out that there is a similar relationship between monadic combinators flatMap and flatten (see next two slides). @philip_schwarz
  • 11. trait Monad[F[_]] { def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B] def unit[A](a: => A): F[A] } flatmap + unit 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] } map + join + unit The join function takes an F[F[A]] and returns an F[A] def join[A](mma: F[F[A]]): F[A] What it does is “remove a layer” of F. The flatMap function takes an F[A] and a function from A to F[B] and returns an F[B] def flatMap[A,B](ma: F[A])(f: A ⇒ F[B]): F[B] What it does is apply to each A element of ma a function f producing an F[B], but instead of returning the resulting F[F[B]], it flattens it and returns an F[B]. Recall two of the three minimal sets of combinators that can be used to define a monad. One set includes flatMap and the other includes join (in Scala this is also known as flatten) @philip_schwarz
  • 12. It turns out that flatMap can be defined in terms of map and flatten def flatMap[A,B](ma: F[A])(f: A ⇒ F[B]): F[B] = flatten(ma map f) and flatten can be defined in terms of flatMap def flatten[A](mma: F[F[A]]): F[A] = flatMap(mma)(x ⇒ x) So flatMapping a function is just mapping the function first and then flattening the result and flattening is just flatMapping the identity function x => x Now recall that traverse can be defined in terms of map and sequence: def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = sequence(a map f) and sequence can be defined in terms of traverse def sequence[A](a: List[Option[A]]): Option[List[A]] = traverse(a)(x ⇒ x) flatMapping is mapping and then flattening - flattening is just flatMapping identity traversing is mapping and then sequencing - sequencing is just traversing with identity So here is the similarity @philip_schwarz
  • 13. @philip_schwarz But there is another similarity: sequence and join are both natural transformations. Before we can look at why that is the case, let’s quickly recap what a natural transformation is. See the following for a less hurried introduction to natural transformations: 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↑List Option 𝜏 = safeHead List Option natural transformation 𝜏 from List to Option 𝜏String List[String] Option[String] List[Int] 𝜏Int List[Char] 𝜏Char …… … Option[Int] Option[Char] length↑Option ∘ 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 } 𝜏 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 = List Naturality Condition the square commutes safeHead ∘ length↑List = length↑Option ∘ safeHead safeHead ∘ (mapList length) = (mapOption length) ∘ safeHead C1 C2 Category of types and functions the square commutes https://www.slideshare.net/pjschwarz/natural-transformations @philip_schwarz
  • 15. trait Functor[F[_]] { def map[A, B](f: A => B): F[A] => F[B] } val listF = new Functor[List] { def map[A,B](f: A => B): List[A] => List[B] = { case head::tail => f(head)::map(f)(tail) case Nil => Nil } } val length: String => Int = s => s.length def safeHead[A]: List[A] => Option[A] = { case head::_ => Some(head) case Nil => None } val mapAndThenTransform: List[String] => Option[Int] = safeHead compose (listF map length) val transformAndThenMap: List[String] => Option[Int] = (optionF map length) compose safeHead assert(mapAndThenTransform(List("abc", "d", "ef")) == transformAndThenMap(List("abc", "d", "ef"))) assert(mapAndThenTransform(List("abc", "d", "ef")) == Some(3)) assert(transformAndThenMap(List("abc", "d", "ef")) == Some(3)) assert(mapAndThenTransform(List()) == transformAndThenMap(List())) assert(mapAndThenTransform(List()) == None) assert(transformAndThenMap(List()) == None) val optionF = new Functor[Option] { def map[A,B](f: A => B): Option[A] => Option[B] = { case Some(a) => Some(f(a)) case None => None } } the square commutes safeHead ∘ length↑List = length↑Option ∘ safeHead safeHead ∘ (mapList length) = (mapOption length) ∘ safeHead 𝜏 List Option mapF lifts f into F so f↑F is map f Concrete Scala Example: safeHead - natural transformation 𝜏 from List functor to Option functor Naturality Condition https://www.slideshare.net/pjschwarz/natural-transformations @philip_schwarz
  • 17. 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 monadic combinators unit and join are natural transformations
  • 18. So is sequence a natural transformation, just like safeHead, unit and join? @philip_schwarz safeHead: List[A] ⇒ Option[A] unit: A ⇒ List[A] join: List[List[A]] ⇒ List[A] sequence: List[Option[A]] ⇒ Option[List[A]] Hmm, sequence differs from the other functions in that its input and output types are nested functors. But wait, the input of join is also a nested functor. Better check with the experts…
  • 19. Asking the experts – exhibit number 1: for sequence, just like for safeHead, first transforming and then mapping is the same as first mapping and then transforming
  • 20. It seems to me that sequence is a natural transformation, like safeHead, but in some higher-order sense. Is that right? def safeHead[A]: List[A] => Option[A] def sequence[A](a: List[Option[A]]): Option[List[A]] • natural transformation safeHead maps the List Functor to the Option Functor • I can either first apply safeHead to a list and then map the length function over the result • Or I can first map the length function over the list and then apply safeHead to the result • The overall result is the same • In the first case, map is used to lift the length function into the List Functor • In the second case, map is used to lift the length function into the Option Functor Is it correct to consider sequence to be a natural transformation? I ask because there seems to be something higher-order about sequence compared to safeHead • natural transformation sequence maps a List Functor of an Option Functor to an Option Functor of a List Functor • I can either first apply sequence to a list of options and then map a function that maps the length function • Or I can first map over the list a function that maps the length function, and then sequence the result • The overall result is the same • In the first case, we first use map to lift the length function into the List Functor and then again to lift the resulting function into the Option Functor, • In the second case, we first use map to lift the length function into the Option Functor and then again to lift the resulting function into the List Functor It seems that for a natural transformation that rearranges N layers of Functors we call map on each of those layers before we apply a function. Asking the experts – exhibit number 2: why sequence seems to be a natural transformation, albeit a more complex one
  • 22. safeHead: List[A] ⇒ Option[A] unit: A ⇒ List[A] join: List[List[A]] ⇒ List[A] sequence: List[Option[A]] ⇒ Option[List[A]] So yes, sequence, like safeHead, unit and join, is a natural transformation. They are all polymorphic functions from one functor to another. @philip_schwarz Let’s illustrate this further in the next slide using diagrams and some code.
  • 23. List[String] List[int] String Int List[List[String]] List[List[Int]] List[String] List[int] Option[String] Option[Int] join join unit safeHeadList length length↑L (length↑L)↑L length↑L length↑O (length↑L)↑L ∘ unit unit ∘length↑L unit unit unit length↑L ∘ unit unit ∘ length length↑L ∘ join join ∘(length↑L)↑L length↑O∘ safeHeadList safeHeadOption ∘ length↑L safeHeadOption Option[List[String]] List[Option[Int]] sequence (length↑O)↑L (length↑L)↑O ∘ sequence sequence ∘ (length ↑ O) ↑ L sequence List[Option[String]] Option[List[Int]] (length↑L)↑O natural transformations: safeHead unit aka pure aka η join aka flatten aka μ sequence map lifts f into F f↑L is map f for F=List f↑O is map f for F=Option val expected = Some(2) // first map and then transform assert(safeHead(List("ab","abc","a").map(_.length)) == expected) // first transform and then map assert((safeHead(List("ab","abc","a")).map(_.length)) == expected) val expected = List(2,3,1) // first map and then transform assert(List(List("ab","abc"),Nil,List("a")).map(_.map(_.length)).flatten == expected) // first transform and then map assert(List(List("ab","abc"),Nil,List("a")).flatten.map(_.length) == expected) val expected = Some(List(2,3,1)) // first map and then transform assert(sequence(List(Some("ab"),Some("abc"),Some("a")).map.(_.map(_.length))) == expected) // first transform and then map assert(sequence(List(Some("ab"),Some("abc"),Some("a"))).map(_.map(_.length)) == expected)
  • 24. Combines a list of Options into one Option containing a list of all the Some values in the original list. If the original list contains None even once, the result of the function is None ; otherwise the result is Some with a list of all the values. def sequence[A] (a: List[Option[ A]]): Option[ List[A]] From sequence for Option to sequence for Either def sequence[E,A](a: List[Either[E,A]]): Either[E,List[A]] Combines a list of Eithers into one Either containing a list of all the Right values in the original list. If the original list contains Left even once, the result of the function is the first Left; otherwise the result is Right with a list of all the values.
  • 25. The sequence function for Either assert( sequence(Nil) == Right(List()) ) assert( sequence(List()) == Right(List()) ) if the list is empty then the result is Right of empty list assert( sequence(List(Right(1))) == Right(List(1)) ) assert( sequence(List(Right(1),Right(2))) == Right(List(1, 2)) ) assert( sequence(List(Right(1),Right(2),Right(3))) == Right(List(1, 2, 3)) ) if the list contains all Right values then the result is Right of a list if the list contains any Left value then the result is the first Left value assert( sequence(List(Left(-1))) == Left(-1) ) assert( sequence(List(Right(1),Left(-2),Right(3))) == Left(-2) ) assert( sequence(List(Left(0),Left(-1),Left(-2))) == Left(0) ) def sequence[E,A](a: List[Either[E,A]]): Either[E,List[A]] Combines a list of Eithers into one Either containing a list of all the Right values in the original list. If the original list contains Left even once, the result of the function is the first Left; otherwise the result is Right with a list of all the values.
  • 26. explicit recursive implementation def sequence[A](a: List[Option[A]]): Option[List[A]] = a match { case Nil => Some(Nil) case h :: t => h flatMap (hh => sequence(t) map (hh :: _)) } def sequence[E,A](a: List[Either[E,A]]): Either[E,List[A]] = a match { case Nil => Right(Nil) case h :: t => h flatMap (hh => sequence(t) map (hh :: _)) } From implementation of sequence for Option to implementation of sequence for Either Implementation using foldRight and map2 def sequence[A](a: List[Option[A]]): Option[List[A]] = a.foldRight[Option[List[A]]](Some(Nil))((h,t) => map2(h,t)(_ :: _)) def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] = a flatMap (aa => b map (bb => f(aa, ab))) def sequence[A,E](a: List[Either[E,A]]): Either[E,List[A]] = a.foldRight[Either[E,List[A]]](Right(Nil))((h,t) => map2(h,t)(_ :: _)) def map2[A,B,C,E](a: Either[E,A], b: Either[E,B])(f: (A, B) => C): Either[E,C] = a flatMap (aa => b map (bb => f(aa, bb)))
  • 27. explicit recursive implementation Implementation using foldRight and map2 From implementation of traverse for Option to implementation of traverse for Either def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a match { case Nil => Some(Nil) case h::t => map2(f(h), traverse(t)(f))(_ :: _) } def traverse[A,B,E](a: List[A])(f: A => Either[E, B]): Either[E,List[B]] = a match { case Nil => Right(Nil) case h::t => map2(f(h),traverse(t)(f))(_ :: _) } def traverse[A,B](a: List[A])(f: A => Option[B]): Option[List[B]] = a.foldRight[Option[List[B]]](Some(Nil))((h,t) => map2(f(h),t)(_ :: _)) def traverse[A,B,E](a: List[A])(f: A => Either[E,B]): Either[E,List[B]] = a.foldRight[Either[E,List[B]]](Right(Nil))((h, t) => map2(f(h),(t))(_ :: _))
  • 28. Simple example of using sequence function, revisited for Either Sometimes we’ll want to map over a list using a function that might fail, returning Left[Throwable] if applying it to any element of the list returns Left[Throwable]. For example, what if we have a whole list of String values that we wish to parse to Either[Throwable,Int]? In that case, we can simply sequence the results of the map. import scala.util.Try def parseIntegers(a: List[String]): Either[Throwable,List[Int]] = sequence(a map (i => Try(i.toInt).toEither)) assert( parseIntegers(List("1", "2", "3")) == Right(List(1, 2, 3) ) assert( parseIntegers(List("1", "x", "3")) == Left(java.lang.NumberFormatException: For input string: "x") ) assert( parseIntegers(List("1", "x", "1.2")) == Left(java.lang.NumberFormatException: For input string: "x") )
  • 29. The close relationship between sequence and traverse, revisited for Either def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = sequence(a map f) def sequence[A](a: List[Option[A]]): Option[List[A]] = traverse(a)(x => x) def traverse[A,B,E](a: List[A])(f: A => Either[E,B]): Either[E,List[B]] = sequence(a map f) def sequence[A,E](a: List[Either[E,A]]): Either[E,List[A]] = traverse(a)(x => x) defining traverse in terms of map and sequence defining sequence in terms of traverse and identity
  • 30. The sequence and traverse functions we have seen so far (and the map and map2 functions they depend on), are specialised for a particular type constructor, e.g. Option or Either. Can they be generalised so that they work on many more type constructors? @philip_schwarz def sequence[A](a: List[Option[A]]): Option[List[A]] = a.foldRight[Option[List[A]]](Some(Nil))((h,t) => map2(h,t)(_ :: _)) def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a.foldRight[Option[List[B]]](Some(Nil))((h,t) => map2(f(h),t)(_ :: _)) def map2[A,B,C](oa: Option[A], ob: Option[B])(f: (A, B) => C): Option[C] = oa flatMap (a => ob map (b => f(a, b))) E.g. in the above example, the Option specific items that sequence, traverse and map2 depend on are Option’s Some constructor, Option’s map function, and Option’s flatMap function. In the case of Either, the dependencies are on Either’s Right constructor, Either’s map function and Either’s flatMap function. Option and Either are monads and every monad has a unit function, a map function, and a flatMap function. Since Some and Right are Option and Either’s unit functions and since map2 can be implemented using map and flatMap, it follows that sequence and traverse can be implemented for every monad. See the next slide for a reminder that every monad has unit, map and flatMap functions.
  • 31. 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 The three different ways of defining a monad, and how a monad always has the following three functions: unit, map, flatMap
  • 32. def sequence[A](a: List[Option[A]]): Option[List[A]] def sequence[A](a: List[F[A]]): F[List[A]] def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] def traverse[A,B](a: List[A])(f: A => F[B]): F[List[B]] def map2[A,B,C](a: F[A], b: F[B])(f: (A, B) => C): F[C] Generalising the signatures of map2, sequence and traverse so they work on a monad F
  • 33. trait Functor[F[_]] { def map[A,B](fa: F[A])(f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { def unit[A](a: => A): F[A] def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B] def map[A,B](ma: F[A])(f: A => B): F[B] = flatMap(ma)(a => unit(f(a))) def map2[A,B,C](ma: F[A], mb: F[B])(f: (A, B) => C): F[C] = flatMap(ma)(a => map(mb)(b => f(a, b))) def sequence[A](lma: List[F[A]]): F[List[A]] = lma.foldRight(unit(List[A]()))((ma, mla) => map2(ma, mla)(_ :: _)) def traverse[A,B](la: List[A])(f: A => F[B]): F[List[B]] = la.foldRight(unit(List[B]()))((a, mlb) => map2(f(a), mlb)(_ :: _)) } Functional Programming in Scala How a monad can define sequence and traverse
  • 34. A simple example of using the traversable function of the Option monad val optionM = new Monad[Option] { def unit[A](a: => A): Option[A] = Some(a) def flatMap[A,B](ma: Option[A])(f: A => Option[B]): Option[B] = ma match { case Some(a) => f(a) case None => None } } def parseIntsMaybe(a: List[String]): Option[List[Int]] = optionM.traverse(a)(i => Try(i.toInt).toOption) scala> parseIntsMaybe(List("1", "2", "3")) res0: Option[List[Int]] = Some(List(1, 2, 3)) scala> parseIntsMaybe(List("1", "x", "3")) res1: Option[List[Int]] = None scala> parseIntsMaybe(List("1", "x", "y")) res2: Option[List[Int]] = None
  • 35. A simple example of using the traversable function of the Either monad type Validated[A] = Either[Throwable,A] val eitherM = new Monad[Validated] { def unit[A](a: => A): Validated[A] = Right(a) def flatMap[A,B](ma: Validated[A])(f: A => Validated[B]): Validated[B] = ma match { case Left(l) => Left(l) case Right(a) => f(a) } } def parseIntsValidated(a: List[String]): Either[Throwable,List[Int]] = eitherM.traverse(a)(i => Try(i.toInt).toEither) scala> parseIntsValidated(List("1", "2", "3")) res0: Either[Throwable,List[Int]] = Right(List(1, 2, 3)) scala> parseIntsValidated(List("1", "x", "3")) res1: Either[Throwable,List[Int]] = Left(java.lang.NumberFormatException: For input string: "x") scala> parseIntsValidated(List("1", "x", "1.2")) res2: Either[Throwable,List[Int]] = Left(java.lang.NumberFormatException: For input string: "x")
  • 36. But is it necessary to use a monad in order to define generic sequence and traverse methods? Find out in part 2. @philip_schwarz