SlideShare ist ein Scribd-Unternehmen logo
1 von 6
Downloaden Sie, um offline zu lesen
a sighting of
traverseFilter
and foldMap in
@philip_schwarz
slides by http://fpilluminated.com/
by
trait ShoppingCart[F[_]] {
def add(userId: UserId, itemId: ItemId, quantity: Quantity): F[Unit]
def get(userId: UserId): F[CartTotal]
def delete(userId: UserId): F[Unit]
def removeItem(userId: UserId, itemId: ItemId): F[Unit]
def update(userId: UserId, cart: Cart): F[Unit]
}
object ShoppingCart {
def make[F[_]: GenUUID: MonadThrow](
items: Items[F],
redis: RedisCommands[F, String, String],
exp: ShoppingCartExpiration
): ShoppingCart[F] = new ShoppingCart[F] {
override def add(userId: UserId, itemId: ItemId, quantity: Quantity): F[Unit] =
redis.hSet(userId.show, itemId.show, quantity.show) *>
redis.expire(userId.show, exp.value).void
override def get(userId: UserId): F[CartTotal] =
redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] =>
itemIdToQuantityMap.toList
.traverseFilter { case (id, qty) =>
for {
itemId <- ID.read[F, ItemId](id)
quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt))
maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity)))
} yield maybeCartItem
}
.map { items =>
CartTotal(items, items.foldMap(_.subTotal))
}
}
…
}
}
with some minor renaming, to ease comprehension
for anyone lacking context – see repo for original
override def get(userId: UserId): F[CartTotal] =
redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] =>
itemIdToQuantityMap.toList
.traverseFilter { case (id, qty) =>
for {
itemId <- ID.read[F, ItemId](id)
quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt))
maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity)))
} yield maybeCartItem
}
.map { items =>
CartTotal(items, items.foldMap(_.subTotal))
}
}
override def get(userId: UserId): F[CartTotal] =
redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] =>
itemIdToQuantityMap.toList
.traverseFilter { case (id, qty) =>
for {
itemId <- ID.read[F, ItemId](id)
quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt))
maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity)))
} yield maybeCartItem
}
.map { items =>
CartTotal(items, items.foldMap(_.subTotal))
}
}
^⇧P Type Info
^⇧P Type Info
override def get(userId: UserId): F[CartTotal] =
redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] =>
itemIdToQuantityMap.toList
.traverseFilter { case (id, qty) =>
for {
itemId <- ID.read[F, ItemId](id)
quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt))
maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity)))
} yield maybeCartItem
}
.map { items: List[cart.CartItem] =>
CartTotal(items, items.foldMap(_.subTotal))
}
}
override def get(userId: UserId): F[CartTotal] =
redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] =>
itemIdToQuantityMap.toList
.traverseFilter { case (id, qty) =>
for {
itemId <- ID.read[F, ItemId](id)
quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt))
maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity)))
} yield maybeCartItem
}
.map { items: List[cart.CartItem] =>
CartTotal(items, items.foldMap(_.subTotal))
}
}
^⇧P Type Info
^⇧P Type Info
@typeclass trait TraverseFilter[F[_]] extends FunctorFilter[F] {
…
A combined traverse and filter. Filtering is handled via Option instead of Boolean such that the
output type B can be different than the input type A.
def traverseFilter[G[_], A, B](fa: F[A])(f: A => G[Option[B]])(implicit G: Applicative[G]): G[F[B]]
List[(String,String)] => ((String,String) => F[Option[CartItem]]) => F[List[CartItem]]
def make[F[_]: GenUUID: MonadThrow]
type MonadThrow[F[_]] = MonadError[F, Throwable]
An applicative that also allows you to raise and or handle an error value.
This type class allows one to abstract over error-handling applicatives.
trait ApplicativeError[F[_], E] extends Applicative[F] { …
This type class allows one to abstract over error-handling monads.
trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] { …
Applicative
Monad
Functor
TraverseFilter
FunctorFilter
ApplicativeError
MonadError
FunctorFilter[F] allows you to map and filter out elements simultaneously.
@typeclass trait FunctorFilter[F[_]] extends Serializable
TraverseFilter, also known as Witherable, represents list-like structures
that can essentially have a traverse and a filter applied as a single
combined operation (traverseFilter).
@typeclass trait TraverseFilter[F[_]] extends FunctorFilter[F] {
Traverse
Foldable
trait ShoppingCart[F[_]] {
def add(userId: UserId, itemId: ItemId, quantity: Quantity): F[Unit]
def get(userId: UserId): F[CartTotal]
def delete(userId: UserId): F[Unit]
def removeItem(userId: UserId, itemId: ItemId): F[Unit]
def update(userId: UserId, cart: Cart): F[Unit]
}
object ShoppingCart {
def make[F[_]: GenUUID: MonadThrow](
items: Items[F],
redis: RedisCommands[F, String, String],
exp: ShoppingCartExpiration
): ShoppingCart[F] = new ShoppingCart[F] {
override def add(userId: UserId, itemId: ItemId, quantity: Quantity): F[Unit] =
redis.hSet(userId.show, itemId.show, quantity.show) *>
redis.expire(userId.show, exp.value).void
override def get(userId: UserId): F[CartTotal] =
redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] =>
itemIdToQuantityMap.toList
.traverseFilter { case (id, qty) =>
for {
itemId <- ID.read[F, ItemId](id)
quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt))
maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity)))
} yield maybeCartItem
}
.map { items: List[cart.CartItem] =>
CartTotal(items, items.foldMap(_.subTotal))
}
}
…
}
}
override def get(userId: UserId): F[CartTotal] =
redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] =>
itemIdToQuantityMap.toList
.traverseFilter { case (id, qty) =>
for {
itemId <- ID.read[F, ItemId](id)
quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt))
maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity)))
} yield maybeCartItem
}
.map { items =>
CartTotal(items, items.foldMap(_.subTotal))
}
}
⇧⌘P Show implicit arguments
trait Foldable[F[_]] extends UnorderedFoldable[F] with FoldableNFunctions[F] {
…
Fold implemented by mapping A values into B and then
combining them using the given Monoid[B] instance.
def foldMap[A, B](fa: F[A])(f: A => B)(implicit B: Monoid[B]): B =
foldLeft(fa, B.empty)((b, a) => B.combine(b, f(a)))
^⇧P Type Info
⌘P Parameter Info
override def get(userId: UserId): F[CartTotal] =
redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] =>
itemIdToQuantityMap.toList
.traverseFilter { case (id, qty) =>
for {
itemId <- ID.read[F, ItemId](id)
quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt))
maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity)))
} yield maybeCartItem
}
.map { items =>
CartTotal(items, items.foldMap(_.subTotal))(moneyMonoid)
}
}
implicit val moneyMonoid: Monoid[Money] =
new Monoid[Money] {
override def empty: Money = USD(0)
override def combine(x: Money, y: Money): Money = x + y
}
case class CartItem(item: Item, quantity: Quantity) {
def subTotal: Money = USD(item.price.amount * quantity.value)
}
List[CartItem] => (CartItem => Money) => Monoid[Money] => Money
A monoid is a semigroup with an identity. A monoid is a specialization of a
semigroup, so its operation must be associative. Additionally, combine(x,
empty) == combine(empty, x) == x. For example, if we have Monoid[String],
with combine as string concatenation, then empty = "".
trait Monoid[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A] {
A semigroup is any set A with an associative operation (combine).
trait Semigroup[@sp(Int, Long, Float, Double) A] extends Any with Serializable {
Semigroup
Monoid
final class Money private
(val amount: BigDecimal)
(val currency: Currency)
extends Quantity[Money] {

Weitere ähnliche Inhalte

Ähnlich wie A sighting of traverseFilter and foldMap in Practical FP in Scala

Algebraic Data Types and Origami Patterns
Algebraic Data Types and Origami PatternsAlgebraic Data Types and Origami Patterns
Algebraic Data Types and Origami Patterns
Vasil Remeniuk
 
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
fasttracksunglass
 
Data type a la carte
Data type a la carteData type a la carte
Data type a la carte
Yun-Yan Chi
 
Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)
stasimus
 

Ähnlich wie A sighting of traverseFilter and foldMap in Practical FP in Scala (20)

Thesis PPT
Thesis PPTThesis PPT
Thesis PPT
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator Pattern
 
Algebraic Data Types and Origami Patterns
Algebraic Data Types and Origami PatternsAlgebraic Data Types and Origami Patterns
Algebraic Data Types and Origami Patterns
 
Practical scalaz
Practical scalazPractical scalaz
Practical scalaz
 
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)
 
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf12.9 Program Online shopping cart (continued) (C++)This program e.pdf
12.9 Program Online shopping cart (continued) (C++)This program e.pdf
 
Blending Culture in Twitter Client
Blending Culture in Twitter ClientBlending Culture in Twitter Client
Blending Culture in Twitter Client
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 
Essence of the iterator pattern
Essence of the iterator patternEssence of the iterator pattern
Essence of the iterator pattern
 
Bar graph
Bar graphBar graph
Bar graph
 
Improving Correctness With Type - Goto Con Berlin
Improving Correctness With Type - Goto Con BerlinImproving Correctness With Type - Goto Con Berlin
Improving Correctness With Type - Goto Con Berlin
 
Functions, Types, Programs and Effects
Functions, Types, Programs and EffectsFunctions, Types, Programs and Effects
Functions, Types, Programs and Effects
 
Testing in the World of Functional Programming
Testing in the World of Functional ProgrammingTesting in the World of Functional Programming
Testing in the World of Functional Programming
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
 
Functional programming techniques in real-world microservices
Functional programming techniques in real-world microservicesFunctional programming techniques in real-world microservices
Functional programming techniques in real-world microservices
 
Functional programming in Scala: From error handling to fp
Functional programming in Scala: From error handling to fpFunctional programming in Scala: From error handling to fp
Functional programming in Scala: From error handling to fp
 
Data type a la carte
Data type a la carteData type a la carte
Data type a la carte
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
 
Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)
 
Monads - Dublin Scala meetup
Monads - Dublin Scala meetupMonads - Dublin Scala meetup
Monads - Dublin Scala meetup
 

Mehr von 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
 

Mehr von 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 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
 
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...
 
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...
 
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 (...
 

Kürzlich hochgeladen

AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
Alluxio, Inc.
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
mbmh111980
 

Kürzlich hochgeladen (20)

KLARNA - Language Models and Knowledge Graphs: A Systems Approach
KLARNA -  Language Models and Knowledge Graphs: A Systems ApproachKLARNA -  Language Models and Knowledge Graphs: A Systems Approach
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
 
How to pick right visual testing tool.pdf
How to pick right visual testing tool.pdfHow to pick right visual testing tool.pdf
How to pick right visual testing tool.pdf
 
Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024
 
IT Software Development Resume, Vaibhav jha 2024
IT Software Development Resume, Vaibhav jha 2024IT Software Development Resume, Vaibhav jha 2024
IT Software Development Resume, Vaibhav jha 2024
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
 
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
 
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
 
SQL Injection Introduction and Prevention
SQL Injection Introduction and PreventionSQL Injection Introduction and Prevention
SQL Injection Introduction and Prevention
 
The Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion ProductionThe Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion Production
 
AI Hackathon.pptx
AI                        Hackathon.pptxAI                        Hackathon.pptx
AI Hackathon.pptx
 
CompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdfCompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdf
 
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAGAI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
 
APVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purityAPVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purity
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdfStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabber
 
Workforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfWorkforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdf
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Kraków
 

A sighting of traverseFilter and foldMap in Practical FP in Scala

  • 1. a sighting of traverseFilter and foldMap in @philip_schwarz slides by http://fpilluminated.com/ by
  • 2. trait ShoppingCart[F[_]] { def add(userId: UserId, itemId: ItemId, quantity: Quantity): F[Unit] def get(userId: UserId): F[CartTotal] def delete(userId: UserId): F[Unit] def removeItem(userId: UserId, itemId: ItemId): F[Unit] def update(userId: UserId, cart: Cart): F[Unit] } object ShoppingCart { def make[F[_]: GenUUID: MonadThrow]( items: Items[F], redis: RedisCommands[F, String, String], exp: ShoppingCartExpiration ): ShoppingCart[F] = new ShoppingCart[F] { override def add(userId: UserId, itemId: ItemId, quantity: Quantity): F[Unit] = redis.hSet(userId.show, itemId.show, quantity.show) *> redis.expire(userId.show, exp.value).void override def get(userId: UserId): F[CartTotal] = redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] => itemIdToQuantityMap.toList .traverseFilter { case (id, qty) => for { itemId <- ID.read[F, ItemId](id) quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt)) maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity))) } yield maybeCartItem } .map { items => CartTotal(items, items.foldMap(_.subTotal)) } } … } } with some minor renaming, to ease comprehension for anyone lacking context – see repo for original
  • 3. override def get(userId: UserId): F[CartTotal] = redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] => itemIdToQuantityMap.toList .traverseFilter { case (id, qty) => for { itemId <- ID.read[F, ItemId](id) quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt)) maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity))) } yield maybeCartItem } .map { items => CartTotal(items, items.foldMap(_.subTotal)) } } override def get(userId: UserId): F[CartTotal] = redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] => itemIdToQuantityMap.toList .traverseFilter { case (id, qty) => for { itemId <- ID.read[F, ItemId](id) quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt)) maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity))) } yield maybeCartItem } .map { items => CartTotal(items, items.foldMap(_.subTotal)) } } ^⇧P Type Info ^⇧P Type Info
  • 4. override def get(userId: UserId): F[CartTotal] = redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] => itemIdToQuantityMap.toList .traverseFilter { case (id, qty) => for { itemId <- ID.read[F, ItemId](id) quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt)) maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity))) } yield maybeCartItem } .map { items: List[cart.CartItem] => CartTotal(items, items.foldMap(_.subTotal)) } } override def get(userId: UserId): F[CartTotal] = redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] => itemIdToQuantityMap.toList .traverseFilter { case (id, qty) => for { itemId <- ID.read[F, ItemId](id) quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt)) maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity))) } yield maybeCartItem } .map { items: List[cart.CartItem] => CartTotal(items, items.foldMap(_.subTotal)) } } ^⇧P Type Info ^⇧P Type Info @typeclass trait TraverseFilter[F[_]] extends FunctorFilter[F] { … A combined traverse and filter. Filtering is handled via Option instead of Boolean such that the output type B can be different than the input type A. def traverseFilter[G[_], A, B](fa: F[A])(f: A => G[Option[B]])(implicit G: Applicative[G]): G[F[B]] List[(String,String)] => ((String,String) => F[Option[CartItem]]) => F[List[CartItem]] def make[F[_]: GenUUID: MonadThrow] type MonadThrow[F[_]] = MonadError[F, Throwable] An applicative that also allows you to raise and or handle an error value. This type class allows one to abstract over error-handling applicatives. trait ApplicativeError[F[_], E] extends Applicative[F] { … This type class allows one to abstract over error-handling monads. trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] { … Applicative Monad Functor TraverseFilter FunctorFilter ApplicativeError MonadError FunctorFilter[F] allows you to map and filter out elements simultaneously. @typeclass trait FunctorFilter[F[_]] extends Serializable TraverseFilter, also known as Witherable, represents list-like structures that can essentially have a traverse and a filter applied as a single combined operation (traverseFilter). @typeclass trait TraverseFilter[F[_]] extends FunctorFilter[F] { Traverse Foldable
  • 5. trait ShoppingCart[F[_]] { def add(userId: UserId, itemId: ItemId, quantity: Quantity): F[Unit] def get(userId: UserId): F[CartTotal] def delete(userId: UserId): F[Unit] def removeItem(userId: UserId, itemId: ItemId): F[Unit] def update(userId: UserId, cart: Cart): F[Unit] } object ShoppingCart { def make[F[_]: GenUUID: MonadThrow]( items: Items[F], redis: RedisCommands[F, String, String], exp: ShoppingCartExpiration ): ShoppingCart[F] = new ShoppingCart[F] { override def add(userId: UserId, itemId: ItemId, quantity: Quantity): F[Unit] = redis.hSet(userId.show, itemId.show, quantity.show) *> redis.expire(userId.show, exp.value).void override def get(userId: UserId): F[CartTotal] = redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] => itemIdToQuantityMap.toList .traverseFilter { case (id, qty) => for { itemId <- ID.read[F, ItemId](id) quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt)) maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity))) } yield maybeCartItem } .map { items: List[cart.CartItem] => CartTotal(items, items.foldMap(_.subTotal)) } } … } }
  • 6. override def get(userId: UserId): F[CartTotal] = redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] => itemIdToQuantityMap.toList .traverseFilter { case (id, qty) => for { itemId <- ID.read[F, ItemId](id) quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt)) maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity))) } yield maybeCartItem } .map { items => CartTotal(items, items.foldMap(_.subTotal)) } } ⇧⌘P Show implicit arguments trait Foldable[F[_]] extends UnorderedFoldable[F] with FoldableNFunctions[F] { … Fold implemented by mapping A values into B and then combining them using the given Monoid[B] instance. def foldMap[A, B](fa: F[A])(f: A => B)(implicit B: Monoid[B]): B = foldLeft(fa, B.empty)((b, a) => B.combine(b, f(a))) ^⇧P Type Info ⌘P Parameter Info override def get(userId: UserId): F[CartTotal] = redis.hGetAll(userId.show).flatMap { itemIdToQuantityMap: Map[String, String] => itemIdToQuantityMap.toList .traverseFilter { case (id, qty) => for { itemId <- ID.read[F, ItemId](id) quantity <- MonadThrow[F].catchNonFatal(Quantity(qty.toInt)) maybeCartItem <- items.findById(itemId).map(_.map(_.cart(quantity))) } yield maybeCartItem } .map { items => CartTotal(items, items.foldMap(_.subTotal))(moneyMonoid) } } implicit val moneyMonoid: Monoid[Money] = new Monoid[Money] { override def empty: Money = USD(0) override def combine(x: Money, y: Money): Money = x + y } case class CartItem(item: Item, quantity: Quantity) { def subTotal: Money = USD(item.price.amount * quantity.value) } List[CartItem] => (CartItem => Money) => Monoid[Money] => Money A monoid is a semigroup with an identity. A monoid is a specialization of a semigroup, so its operation must be associative. Additionally, combine(x, empty) == combine(empty, x) == x. For example, if we have Monoid[String], with combine as string concatenation, then empty = "". trait Monoid[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A] { A semigroup is any set A with an associative operation (combine). trait Semigroup[@sp(Int, Long, Float, Double) A] extends Any with Serializable { Semigroup Monoid final class Money private (val amount: BigDecimal) (val currency: Currency) extends Quantity[Money] {