A sighting of traverseFilter and foldMap in Practical FP in Scala

Philip Schwarz
Philip SchwarzSoftware development is what I am passionate about
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] {
1 of 6

Recommended

Scala Left Fold Parallelisation - Three Approaches by
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three ApproachesPhilip Schwarz
39 views44 slides
Tagless Final Encoding - Algebras and Interpreters and also Programs by
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 ProgramsPhilip Schwarz
45 views16 slides
Fusing Transformations of Strict Scala Collections with Views by
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsPhilip Schwarz
20 views28 slides
A sighting of sequence function in Practical FP in Scala by
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 ScalaPhilip Schwarz
27 views4 slides
N-Queens Combinatorial Puzzle meets Cats by
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsPhilip Schwarz
33 views386 slides
Kleisli composition, flatMap, join, map, unit - implementation and interrelat... by
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...Philip Schwarz
10 views16 slides

More Related Content

More from Philip Schwarz

Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha... by
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...Philip Schwarz
826 views20 slides
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t... by
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...Philip Schwarz
1.2K views46 slides
Jordan Peterson - The pursuit of meaning and related ethical axioms by
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 axiomsPhilip Schwarz
141 views12 slides
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c... by
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...Philip Schwarz
100 views1 slide
Defining filter using (a) recursion (b) folding with S, B and I combinators (... by
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 (...Philip Schwarz
78 views1 slide
The Sieve of Eratosthenes - Part 1 - with minor corrections by
The Sieve of Eratosthenes - Part 1 - with minor correctionsThe Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor correctionsPhilip Schwarz
110 views50 slides

More from Philip Schwarz(20)

Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha... by Philip Schwarz
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...
Philip Schwarz826 views
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t... by Philip Schwarz
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...
Philip Schwarz1.2K views
Jordan Peterson - The pursuit of meaning and related ethical axioms by Philip Schwarz
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
Philip Schwarz141 views
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c... by Philip Schwarz
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...
Philip Schwarz100 views
Defining filter using (a) recursion (b) folding with S, B and I combinators (... by Philip Schwarz
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 (...
Philip Schwarz78 views
The Sieve of Eratosthenes - Part 1 - with minor corrections by Philip Schwarz
The Sieve of Eratosthenes - Part 1 - with minor correctionsThe Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor corrections
Philip Schwarz110 views
The Sieve of Eratosthenes - Part 1 by Philip Schwarz
The Sieve of Eratosthenes - Part 1The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1
Philip Schwarz1K views
The Uniform Access Principle by Philip Schwarz
The Uniform Access PrincipleThe Uniform Access Principle
The Uniform Access Principle
Philip Schwarz542 views
Computer Graphics in Java and Scala - Part 1b by Philip Schwarz
Computer Graphics in Java and Scala - Part 1bComputer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1b
Philip Schwarz261 views
The Expression Problem - Part 2 by Philip Schwarz
The Expression Problem - Part 2The Expression Problem - Part 2
The Expression Problem - Part 2
Philip Schwarz650 views
Computer Graphics in Java and Scala - Part 1 by Philip Schwarz
Computer Graphics in Java and Scala - Part 1Computer Graphics in Java and Scala - Part 1
Computer Graphics in Java and Scala - Part 1
Philip Schwarz403 views
The Expression Problem - Part 1 by Philip Schwarz
The Expression Problem - Part 1The Expression Problem - Part 1
The Expression Problem - Part 1
Philip Schwarz1.2K views
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac... by Philip Schwarz
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Philip Schwarz268 views
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ... by Philip Schwarz
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Philip Schwarz312 views
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ... by Philip Schwarz
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Philip Schwarz630 views
‘go-to’ general-purpose sequential collections - from Java To Scala by Philip Schwarz
‘go-to’ general-purpose sequential collections -from Java To Scala‘go-to’ general-purpose sequential collections -from Java To Scala
‘go-to’ general-purpose sequential collections - from Java To Scala
Philip Schwarz767 views
The Functional Programming Triad of Map, Filter and Fold by Philip Schwarz
The Functional Programming Triad of Map, Filter and FoldThe Functional Programming Triad of Map, Filter and Fold
The Functional Programming Triad of Map, Filter and Fold
Philip Schwarz3.5K views
Functional Core and Imperative Shell - Game of Life Example - Haskell and Scala by Philip Schwarz
Functional Core and Imperative Shell - Game of Life Example - Haskell and ScalaFunctional Core and Imperative Shell - Game of Life Example - Haskell and Scala
Functional Core and Imperative Shell - Game of Life Example - Haskell and Scala
Philip Schwarz824 views
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit - Haskell and... by Philip Schwarz
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit - Haskell and...N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit - Haskell and...
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit - Haskell and...
Philip Schwarz495 views
Quicksort - a whistle-stop tour of the algorithm in five languages and four p... by Philip Schwarz
Quicksort - a whistle-stop tour of the algorithm in five languages and four p...Quicksort - a whistle-stop tour of the algorithm in five languages and four p...
Quicksort - a whistle-stop tour of the algorithm in five languages and four p...
Philip Schwarz1.2K views

Recently uploaded

Applying Platform Engineering Thinking to Observability.pdf by
Applying Platform Engineering Thinking to Observability.pdfApplying Platform Engineering Thinking to Observability.pdf
Applying Platform Engineering Thinking to Observability.pdfNatan Yellin
12 views16 slides
SAP FOR CONTRACT MANUFACTURING.pdf by
SAP FOR CONTRACT MANUFACTURING.pdfSAP FOR CONTRACT MANUFACTURING.pdf
SAP FOR CONTRACT MANUFACTURING.pdfVirendra Rai, PMP
11 views2 slides
DSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - Prida by
DSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - PridaDSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - Prida
DSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - PridaDeltares
17 views9 slides
Consulting for Data Monetization Maximizing the Profit Potential of Your Data... by
Consulting for Data Monetization Maximizing the Profit Potential of Your Data...Consulting for Data Monetization Maximizing the Profit Potential of Your Data...
Consulting for Data Monetization Maximizing the Profit Potential of Your Data...Flexsin
15 views10 slides
DevsRank by
DevsRankDevsRank
DevsRankdevsrank786
10 views1 slide
El Arte de lo Possible by
El Arte de lo PossibleEl Arte de lo Possible
El Arte de lo PossibleNeo4j
34 views35 slides

Recently uploaded(20)

Applying Platform Engineering Thinking to Observability.pdf by Natan Yellin
Applying Platform Engineering Thinking to Observability.pdfApplying Platform Engineering Thinking to Observability.pdf
Applying Platform Engineering Thinking to Observability.pdf
Natan Yellin12 views
DSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - Prida by Deltares
DSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - PridaDSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - Prida
DSD-INT 2023 Dam break simulation in Derna (Libya) using HydroMT_SFINCS - Prida
Deltares17 views
Consulting for Data Monetization Maximizing the Profit Potential of Your Data... by Flexsin
Consulting for Data Monetization Maximizing the Profit Potential of Your Data...Consulting for Data Monetization Maximizing the Profit Potential of Your Data...
Consulting for Data Monetization Maximizing the Profit Potential of Your Data...
Flexsin 15 views
El Arte de lo Possible by Neo4j
El Arte de lo PossibleEl Arte de lo Possible
El Arte de lo Possible
Neo4j34 views
Neo4j y GenAI by Neo4j
Neo4j y GenAI Neo4j y GenAI
Neo4j y GenAI
Neo4j35 views
How to Make the Most of Regression and Unit Testing.pdf by Abhay Kumar
How to Make the Most of Regression and Unit Testing.pdfHow to Make the Most of Regression and Unit Testing.pdf
How to Make the Most of Regression and Unit Testing.pdf
Abhay Kumar10 views
DSD-INT 2023 - Delft3D User Days - Welcome - Day 3 - Afternoon by Deltares
DSD-INT 2023 - Delft3D User Days - Welcome - Day 3 - AfternoonDSD-INT 2023 - Delft3D User Days - Welcome - Day 3 - Afternoon
DSD-INT 2023 - Delft3D User Days - Welcome - Day 3 - Afternoon
Deltares11 views
Dev-Cloud Conference 2023 - Continuous Deployment Showdown: Traditionelles CI... by Marc Müller
Dev-Cloud Conference 2023 - Continuous Deployment Showdown: Traditionelles CI...Dev-Cloud Conference 2023 - Continuous Deployment Showdown: Traditionelles CI...
Dev-Cloud Conference 2023 - Continuous Deployment Showdown: Traditionelles CI...
Marc Müller31 views
How to Install and Activate Email-Researcher by eGrabber
How to Install and Activate Email-ResearcherHow to Install and Activate Email-Researcher
How to Install and Activate Email-Researcher
eGrabber19 views
DSD-INT 2023 Modelling litter in the Yarra and Maribyrnong Rivers (Australia)... by Deltares
DSD-INT 2023 Modelling litter in the Yarra and Maribyrnong Rivers (Australia)...DSD-INT 2023 Modelling litter in the Yarra and Maribyrnong Rivers (Australia)...
DSD-INT 2023 Modelling litter in the Yarra and Maribyrnong Rivers (Australia)...
Deltares9 views
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ... by Deltares
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...
Deltares9 views
Neo4j : Graphes de Connaissance, IA et LLMs by Neo4j
Neo4j : Graphes de Connaissance, IA et LLMsNeo4j : Graphes de Connaissance, IA et LLMs
Neo4j : Graphes de Connaissance, IA et LLMs
Neo4j46 views
Geospatial Synergy: Amplifying Efficiency with FME & Esri ft. Peak Guest Spea... by Safe Software
Geospatial Synergy: Amplifying Efficiency with FME & Esri ft. Peak Guest Spea...Geospatial Synergy: Amplifying Efficiency with FME & Esri ft. Peak Guest Spea...
Geospatial Synergy: Amplifying Efficiency with FME & Esri ft. Peak Guest Spea...
Safe Software391 views
DSD-INT 2023 Simulation of Coastal Hydrodynamics and Water Quality in Hong Ko... by Deltares
DSD-INT 2023 Simulation of Coastal Hydrodynamics and Water Quality in Hong Ko...DSD-INT 2023 Simulation of Coastal Hydrodynamics and Water Quality in Hong Ko...
DSD-INT 2023 Simulation of Coastal Hydrodynamics and Water Quality in Hong Ko...
Deltares10 views
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J... by Deltares
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
Deltares7 views
Software testing company in India.pptx by SakshiPatel82
Software testing company in India.pptxSoftware testing company in India.pptx
Software testing company in India.pptx
SakshiPatel827 views

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] {