SlideShare a Scribd company logo
1 of 36
Download to read offline
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
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
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

What's hot (20)

non-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersnon-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parameters
 
A Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIOA Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIO
 
ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
 
Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)
 
Functional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayFunctional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 Way
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
 
Monadic Java
Monadic JavaMonadic Java
Monadic Java
 
Functional Error Handling with Cats
Functional Error Handling with CatsFunctional Error Handling with Cats
Functional Error Handling with Cats
 
Peeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdfPeeking inside the engine of ZIO SQL.pdf
Peeking inside the engine of ZIO SQL.pdf
 
Ad hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and CatsAd hoc Polymorphism using Type Classes and Cats
Ad hoc Polymorphism using Type Classes and Cats
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorld
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in Scala
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
 
Exploring ZIO Prelude: The game changer for typeclasses in Scala
Exploring ZIO Prelude: The game changer for typeclasses in ScalaExploring ZIO Prelude: The game changer for typeclasses in Scala
Exploring ZIO Prelude: The game changer for typeclasses in Scala
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Boost your productivity with Scala tooling!
Boost your productivity  with Scala tooling!Boost your productivity  with Scala tooling!
Boost your productivity with Scala tooling!
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
 
Java abstract class & abstract methods
Java abstract class & abstract methodsJava abstract class & abstract methods
Java abstract class & abstract methods
 
Java 8 Lambda Expressions
Java 8 Lambda ExpressionsJava 8 Lambda Expressions
Java 8 Lambda Expressions
 

Similar to Sequence and Traverse - Part 1

Monad presentation scala as a category
Monad presentation   scala as a categoryMonad presentation   scala as a category
Monad presentation scala as a category
samthemonad
 
Modular Module Systems
Modular Module SystemsModular Module Systems
Modular Module Systems
league
 

Similar to Sequence and Traverse - Part 1 (20)

Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2
 
Functor Composition
Functor CompositionFunctor Composition
Functor Composition
 
Sequence and Traverse - Part 3
Sequence and Traverse - Part 3Sequence and Traverse - Part 3
Sequence and Traverse - Part 3
 
Frp2016 3
Frp2016 3Frp2016 3
Frp2016 3
 
Scala. Introduction to FP. Monads
Scala. Introduction to FP. MonadsScala. Introduction to FP. Monads
Scala. Introduction to FP. Monads
 
Fp in scala part 1
Fp in scala part 1Fp in scala part 1
Fp in scala part 1
 
Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
 
purrr.pdf
purrr.pdfpurrr.pdf
purrr.pdf
 
Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scala
 
Fp in scala with adts
Fp in scala with adtsFp in scala with adts
Fp in scala with adts
 
Monads and friends demystified
Monads and friends demystifiedMonads and friends demystified
Monads and friends demystified
 
Python for R users
Python for R usersPython for R users
Python for R users
 
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
 
Oh, All the things you'll traverse
Oh, All the things you'll traverseOh, All the things you'll traverse
Oh, All the things you'll traverse
 
Monad presentation scala as a category
Monad presentation   scala as a categoryMonad presentation   scala as a category
Monad presentation scala as a category
 
Scala collection methods flatMap and flatten are more powerful than monadic f...
Scala collection methods flatMap and flatten are more powerful than monadic f...Scala collection methods flatMap and flatten are more powerful than monadic f...
Scala collection methods flatMap and flatten are more powerful than monadic f...
 
Why Haskell Matters
Why Haskell MattersWhy Haskell Matters
Why Haskell Matters
 
Modular Module Systems
Modular Module SystemsModular Module Systems
Modular Module Systems
 
Thesis
ThesisThesis
Thesis
 

More from Philip Schwarz

N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
Philip Schwarz
 

More from Philip Schwarz (20)

Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Folding Cheat Sheet #3 - third in a series
Folding Cheat Sheet #3 - third in a seriesFolding Cheat Sheet #3 - third in a series
Folding Cheat Sheet #3 - third in a series
 
Folding Cheat Sheet #2 - second in a series
Folding Cheat Sheet #2 - second in a seriesFolding Cheat Sheet #2 - second in a series
Folding Cheat Sheet #2 - second in a series
 
Folding Cheat Sheet #1 - first in a series
Folding Cheat Sheet #1 - first in a seriesFolding Cheat Sheet #1 - first in a series
Folding Cheat Sheet #1 - first in a series
 
Scala Left Fold Parallelisation - Three Approaches
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three Approaches
 
Tagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also ProgramsTagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also Programs
 
Fusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with Views
 
A sighting of traverse_ function in Practical FP in Scala
A sighting of traverse_ function in Practical FP in ScalaA sighting of traverse_ function in Practical FP in Scala
A sighting of traverse_ function in Practical FP in Scala
 
A sighting of traverseFilter and foldMap in Practical FP in Scala
A sighting of traverseFilter and foldMap in Practical FP in ScalaA sighting of traverseFilter and foldMap in Practical FP in Scala
A sighting of traverseFilter and foldMap in Practical FP in Scala
 
A sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in ScalaA sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in Scala
 
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
 
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
 
Jordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axiomsJordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axioms
 
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
 
Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...
 

Recently uploaded

Recently uploaded (20)

Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdfThe Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
%in Durban+277-882-255-28 abortion pills for sale in Durban
%in Durban+277-882-255-28 abortion pills for sale in Durban%in Durban+277-882-255-28 abortion pills for sale in Durban
%in Durban+277-882-255-28 abortion pills for sale in Durban
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
SHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationSHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions Presentation
 

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
  • 4.
  • 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
  • 21.
  • 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