This talk was presented on Aug 3rd 2023 during the Scala in the City event a ITV in London https://www.meetup.com/scala-in-the-city/events/292844968/
Visit the following for a description, slideshow, all slides with transcript, pdf, github repo, and eventually a video recording: http://fpilluminated.com/assets/n-queens-combinatorial-puzzle-meets-cats.html
At the centre of this talk is the N-Queens combinatorial puzzle. The reason why this puzzle features in the Scala book and functional programming course by Martin Odersky (the language’s creator), is that such puzzles are a particularly suitable application area of 'for comprehensions'.
We’ll start by (re)acquainting ourselves with the puzzle, and seeing the role played in it by permutations. Next, we’ll see how, when wanting to visualise candidate puzzle solutions, Cats’ monoidal functions fold and foldMap are a great fit for combining images.
While we are all very familiar with the triad providing the bread, butter and jam of functional programming, i.e. map, filter and fold, not everyone knows about the corresponding functions in Cats’ monadic variant of the triad, i.e. mapM, filterM and foldM, which we are going to learn about next.
As is often the case in functional programming, the traverse function makes an appearance, and we shall grab the opportunity to point out the symmetry that exists in the interrelation of flatMap / foldMap / traverse and flatten / fold / sequence.
Armed with an understanding of foldM, we then look at how such a function can be used to implement an iterative algorithm for the N-Queens puzzle.
The talk ends by pointing out that the iterative algorithm is smarter than the recursive one, because it ‘remembers’ where it has already placed previous queens.
Download for better quality.
Monads do not Compose. Not in a generic way - There is no general way of composing monads.
A comment from Rúnar Bjarnason, coauthor of FP in Scala: "They do compose in a different generic way. For any two monads F and G we can take the coproduct which is roughly Free of Either F or G (up to normalization)".
Another comment from Sergei Winitzki (which caused me to upload https://www.slideshare.net/pjschwarz/addendum-to-monads-do-not-compose): "It is a mistake to think that a traversable monad can be composed with another monad. It is true that, given `Traversable`, you can implement the monad's methods (pure and flatMap) for the composition with another monad (as in your slides 21 to 26), but this is a deceptive appearance. The laws of the `Traversable` typeclass are far insufficient to guarantee the laws of the resulting composed monad. The only traversable monads that work correctly are Option, Either, and Writer. It is true that you can implement the type signature of the `swap` function for any `Traversable` monad. However, the `swap` function for monads needs to satisfy very different and stronger laws than the `sequence` function from the `Traversable` type class. I'll have to look at the "Book of Monads"; but, if my memory serves, the FPiS book does not derive any of these laws." See https://www.linkedin.com/feed/update/urn:li:groupPost:41001-6523141414614814720?commentUrn=urn%3Ali%3Acomment%3A%28groupPost%3A41001-6523141414614814720%2C6532108273053761536%29
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...Philip Schwarz
First see the problem solved using the List monad and a Scala for comprehension.
Then see the Scala program translated into Haskell, both using a do expressions and using a List comprehension.
Understand how the Scala for comprehension is desugared, and what role the withFilter function plays.
Also understand how the Haskell do expressions and List comprehension are desugared, and what role the guard function plays.
Scala code for Part 1: https://github.com/philipschwarz/n-queens-combinatorial-problem-scala-part-1
Errata: on slide 30, the resulting lists should be Haskell ones rather than Scala ones.
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
Http4s, Doobie and Circe: The Functional Web StackGaryCoady
Http4s, Doobie and Circe together form a nice platform for building web services. This presentations provides an introduction to using them to build your own service.
(video and more at http://fsharpforfunandprofit.com/fppatterns)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID. The functional programming community has design patterns and principles as well. This talk will provide an overview of some of these patterns (such as currying, monads), and present some demonstrations of FP design in practice. We'll also look at some of the ways you can use these patterns as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
The lazy programmer's guide to writing thousands of testsScott Wlaschin
We are all familiar with example-based testing, as typified by TDD and BDD, where each test is hand-crafted.
But there's another approach to writing tests. In the "property-based testing" approach, a single test is run hundreds of times with randomly generated inputs. Property-based testing is a great way to find edge cases, and also helps you to understand and document the behavior of your code under all conditions.
This talk will introduce property-based testing, show you how it works, and demonstrate why you should consider adding this powerful technique to your toolbelt.
Download for better quality.
Monads do not Compose. Not in a generic way - There is no general way of composing monads.
A comment from Rúnar Bjarnason, coauthor of FP in Scala: "They do compose in a different generic way. For any two monads F and G we can take the coproduct which is roughly Free of Either F or G (up to normalization)".
Another comment from Sergei Winitzki (which caused me to upload https://www.slideshare.net/pjschwarz/addendum-to-monads-do-not-compose): "It is a mistake to think that a traversable monad can be composed with another monad. It is true that, given `Traversable`, you can implement the monad's methods (pure and flatMap) for the composition with another monad (as in your slides 21 to 26), but this is a deceptive appearance. The laws of the `Traversable` typeclass are far insufficient to guarantee the laws of the resulting composed monad. The only traversable monads that work correctly are Option, Either, and Writer. It is true that you can implement the type signature of the `swap` function for any `Traversable` monad. However, the `swap` function for monads needs to satisfy very different and stronger laws than the `sequence` function from the `Traversable` type class. I'll have to look at the "Book of Monads"; but, if my memory serves, the FPiS book does not derive any of these laws." See https://www.linkedin.com/feed/update/urn:li:groupPost:41001-6523141414614814720?commentUrn=urn%3Ali%3Acomment%3A%28groupPost%3A41001-6523141414614814720%2C6532108273053761536%29
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...Philip Schwarz
First see the problem solved using the List monad and a Scala for comprehension.
Then see the Scala program translated into Haskell, both using a do expressions and using a List comprehension.
Understand how the Scala for comprehension is desugared, and what role the withFilter function plays.
Also understand how the Haskell do expressions and List comprehension are desugared, and what role the guard function plays.
Scala code for Part 1: https://github.com/philipschwarz/n-queens-combinatorial-problem-scala-part-1
Errata: on slide 30, the resulting lists should be Haskell ones rather than Scala ones.
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
Http4s, Doobie and Circe: The Functional Web StackGaryCoady
Http4s, Doobie and Circe together form a nice platform for building web services. This presentations provides an introduction to using them to build your own service.
(video and more at http://fsharpforfunandprofit.com/fppatterns)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID. The functional programming community has design patterns and principles as well. This talk will provide an overview of some of these patterns (such as currying, monads), and present some demonstrations of FP design in practice. We'll also look at some of the ways you can use these patterns as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
The lazy programmer's guide to writing thousands of testsScott Wlaschin
We are all familiar with example-based testing, as typified by TDD and BDD, where each test is hand-crafted.
But there's another approach to writing tests. In the "property-based testing" approach, a single test is run hundreds of times with randomly generated inputs. Property-based testing is a great way to find edge cases, and also helps you to understand and document the behavior of your code under all conditions.
This talk will introduce property-based testing, show you how it works, and demonstrate why you should consider adding this powerful technique to your toolbelt.
Functional Programming Patterns (NDC London 2014)Scott Wlaschin
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
download for better quality - Learn about the sequence and traverse functions
through the work of Runar Bjarnason and Paul Chiusano, authors of Functional Programming in Scala https://www.manning.com/books/functional-programming-in-scala
Domain Modeling Made Functional (KanDDDinsky 2019)Scott Wlaschin
(video at https://fsharpforfunandprofit.com/ddd/)
Statically typed functional programming languages encourage a very different way of thinking about types. The type system is your friend, not an annoyance, and can be used in many ways that might not be familiar to OO programmers. Types can be used to represent the domain in a fine-grained, self documenting way. And in many cases, types can even be used to encode business rules so that you literally cannot create incorrect code. You can then use the static type checking almost as an instant unit test — making sure that your code is correct at compile time. In this talk, we'll look at some of the ways you can use types as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
Functional programming can be an excellent approach to designing decoupled, reusable systems with a rich domain model. In fact, the lessons from applying DDD in a functional language translate well to object-oriented programming.
Domain Driven Design with the F# type System -- NDC London 2013Scott Wlaschin
(Video of these slides here http://fsharpforfunandprofit.com/ddd)
Statically typed functional programming languages like F# encourage a very different way of thinking about types. The type system is your friend, not an annoyance, and can be used in many ways that might not be familiar to OO programmers.
Types can be used to represent the domain in a fine-grained, self documenting way. And in many cases, types can even be used to encode business rules so that you literally cannot create incorrect code. You can then use the static type checking almost as an instant unit test — making sure that your code is correct at compile time.
In this talk, we'll look at some of the ways you can use types as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
Code, links to video, etc., at http://fsharpforfunandprofit.com/ddd
For more on DDD and F# see:
http://fsharpforfunandprofit.com/ddd/
http://tomasp.net/blog/type-first-development.aspx/
http://gorodinski.com/blog/2013/02/17/domain-driven-design-with-fsharp-and-eventstore/
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...Philip Schwarz
(download for perfect quality) - See how recursive functions and structural induction relate to recursive datatypes.
Follow along as the fold abstraction is introduced and explained.
Watch as folding is used to simplify the definition of recursive functions over recursive datatypes
Part 1 - through the work of Richard Bird and Graham Hutton.
This version corrects the following issues:
slide 7, 11 fib(0) is 0,rather than 1
slide 23: was supposed to be followed by 2-3 slides recapitulating definitions of factorial and fibonacci with and without foldr, plus translation to scala
slide 36: concat not invoked in concat example
slides 48 and 49: unwanted 'm' in definition of sum
throughout: a couple of typographical errors
throughout: several aesthetic imperfections (wrong font, wrong font colour)
Purely functional libraries like ZIO can help you build high-performance, concurrent applications that don’t have deadlocks, don’t leak resources using purely functional code.
In this talk, Wiem will walk you through how to build a control system for the elevators at a fictional hotel, H&A Hotel. You’ll learn how to use basic control structures like Ref, Queue, STM and ZIO to build real world software.
Implicit parameters, when to use them (or not)!Julien Truffaut
Implicits values are one of the unique features of Scala but they are very complex and easy to misuse. So in this talk we will discuss various valid use cases and anti-pattern for implicits.
You don’t need to be a Scala expert, I will also present how implicit works at high level.
The Functional Programmer's Toolkit (NDC London 2019)Scott Wlaschin
(slides and video at https://fsharpforfunandprofit.com/fptoolkit)
The functional programming community has a number of patterns with strange names such as monads, monoids, functors, and catamorphisms.
In this beginner-friendly talk, we'll demystify these techniques and see how they all fit together into a small but versatile "tool kit".
We'll then see how the tools in this tool kit can be applied to a wide variety of programming problems, such as handling missing data, working with lists, and implementing the functional equivalent of dependency injection.
Some parts of our applications don't need to be asynchronous or interact with the outside world: it's enough that they are stateful, possibly with the ability to handle failure, context, and logging. Although you can use ZIO 2 or monad transformers for this task, both come with drawbacks. In this presentation, Jorge Vásquez will introduce you to ZPure, a data type from ZIO Prelude, which lets you scale back on the power of ZIO 2, but with the same high-performance, type-inference, and ergonomics you expect from ZIO 2 libraries.
Free Monads are a powerful technique that can separate the representation of programs from the messy details of how they get run.
I'll go into the details of how they work, how to use them for fun and profit in your own code, and demonstrate a live Free Monad-driven tank game.
Supporting code at https://github.com/kenbot/free
(Video of these slides here http://fsharpforfunandprofit.com/rop)
(My response to "this is just Either" here: http://fsharpforfunandprofit.com/rop/#monads)
Many examples in functional programming assume that you are always on the "happy path". But to create a robust real world application you must deal with validation, logging, network and service errors, and other annoyances.
So, how do you handle all this in a clean functional way? This talk will provide a brief introduction to this topic, using a fun and easy-to-understand railway analogy.
Be Smart, Constrain Your Types to Free Your Brain!Jorge Vásquez
Admit it: You have used a String to model email values, even though most strings aren’t valid emails (don’t worry, we all have!). Imprecise data models are easy, but they crash applications and corrupt external systems. On the other hand, precise data models take time and generate boilerplate.
So-called newtype libraries have stepped up to the challenge, making it easier to model data precisely using runtime validation. However, newtype libraries aren’t able to validate constants at compile-time, and they don’t generally work with Scala 3.
Enter ZIO Prelude Smart Types, which make it simple to model data types precisely, without any boilerplate, runtime overhead, or compile-time overhead. ZIO Prelude Smart Types work at compile-time and runtime, and they have a uniform API across Scala 2 & 3.
Join ZIO Prelude contributor Jorge Vásquez as he teaches you how to be smart, and constrain your types to free your brain!
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidPhilip Schwarz
The subject of this deck is the small Print[A] program in the following blog post by Noel Welsh: https://www.inner-product.com/posts/direct-style-effects/.
Keywords: "direct-style", "context function", "context functions", "algebraic effect", "algebraic effects", "scala", "effect system", "effect systems", "effect", "side effect", "composition", "fp", "functional programming"
Functional Programming Patterns (NDC London 2014)Scott Wlaschin
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
download for better quality - Learn about the sequence and traverse functions
through the work of Runar Bjarnason and Paul Chiusano, authors of Functional Programming in Scala https://www.manning.com/books/functional-programming-in-scala
Domain Modeling Made Functional (KanDDDinsky 2019)Scott Wlaschin
(video at https://fsharpforfunandprofit.com/ddd/)
Statically typed functional programming languages encourage a very different way of thinking about types. The type system is your friend, not an annoyance, and can be used in many ways that might not be familiar to OO programmers. Types can be used to represent the domain in a fine-grained, self documenting way. And in many cases, types can even be used to encode business rules so that you literally cannot create incorrect code. You can then use the static type checking almost as an instant unit test — making sure that your code is correct at compile time. In this talk, we'll look at some of the ways you can use types as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
Functional programming can be an excellent approach to designing decoupled, reusable systems with a rich domain model. In fact, the lessons from applying DDD in a functional language translate well to object-oriented programming.
Domain Driven Design with the F# type System -- NDC London 2013Scott Wlaschin
(Video of these slides here http://fsharpforfunandprofit.com/ddd)
Statically typed functional programming languages like F# encourage a very different way of thinking about types. The type system is your friend, not an annoyance, and can be used in many ways that might not be familiar to OO programmers.
Types can be used to represent the domain in a fine-grained, self documenting way. And in many cases, types can even be used to encode business rules so that you literally cannot create incorrect code. You can then use the static type checking almost as an instant unit test — making sure that your code is correct at compile time.
In this talk, we'll look at some of the ways you can use types as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
Code, links to video, etc., at http://fsharpforfunandprofit.com/ddd
For more on DDD and F# see:
http://fsharpforfunandprofit.com/ddd/
http://tomasp.net/blog/type-first-development.aspx/
http://gorodinski.com/blog/2013/02/17/domain-driven-design-with-fsharp-and-eventstore/
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...Philip Schwarz
(download for perfect quality) - See how recursive functions and structural induction relate to recursive datatypes.
Follow along as the fold abstraction is introduced and explained.
Watch as folding is used to simplify the definition of recursive functions over recursive datatypes
Part 1 - through the work of Richard Bird and Graham Hutton.
This version corrects the following issues:
slide 7, 11 fib(0) is 0,rather than 1
slide 23: was supposed to be followed by 2-3 slides recapitulating definitions of factorial and fibonacci with and without foldr, plus translation to scala
slide 36: concat not invoked in concat example
slides 48 and 49: unwanted 'm' in definition of sum
throughout: a couple of typographical errors
throughout: several aesthetic imperfections (wrong font, wrong font colour)
Purely functional libraries like ZIO can help you build high-performance, concurrent applications that don’t have deadlocks, don’t leak resources using purely functional code.
In this talk, Wiem will walk you through how to build a control system for the elevators at a fictional hotel, H&A Hotel. You’ll learn how to use basic control structures like Ref, Queue, STM and ZIO to build real world software.
Implicit parameters, when to use them (or not)!Julien Truffaut
Implicits values are one of the unique features of Scala but they are very complex and easy to misuse. So in this talk we will discuss various valid use cases and anti-pattern for implicits.
You don’t need to be a Scala expert, I will also present how implicit works at high level.
The Functional Programmer's Toolkit (NDC London 2019)Scott Wlaschin
(slides and video at https://fsharpforfunandprofit.com/fptoolkit)
The functional programming community has a number of patterns with strange names such as monads, monoids, functors, and catamorphisms.
In this beginner-friendly talk, we'll demystify these techniques and see how they all fit together into a small but versatile "tool kit".
We'll then see how the tools in this tool kit can be applied to a wide variety of programming problems, such as handling missing data, working with lists, and implementing the functional equivalent of dependency injection.
Some parts of our applications don't need to be asynchronous or interact with the outside world: it's enough that they are stateful, possibly with the ability to handle failure, context, and logging. Although you can use ZIO 2 or monad transformers for this task, both come with drawbacks. In this presentation, Jorge Vásquez will introduce you to ZPure, a data type from ZIO Prelude, which lets you scale back on the power of ZIO 2, but with the same high-performance, type-inference, and ergonomics you expect from ZIO 2 libraries.
Free Monads are a powerful technique that can separate the representation of programs from the messy details of how they get run.
I'll go into the details of how they work, how to use them for fun and profit in your own code, and demonstrate a live Free Monad-driven tank game.
Supporting code at https://github.com/kenbot/free
(Video of these slides here http://fsharpforfunandprofit.com/rop)
(My response to "this is just Either" here: http://fsharpforfunandprofit.com/rop/#monads)
Many examples in functional programming assume that you are always on the "happy path". But to create a robust real world application you must deal with validation, logging, network and service errors, and other annoyances.
So, how do you handle all this in a clean functional way? This talk will provide a brief introduction to this topic, using a fun and easy-to-understand railway analogy.
Be Smart, Constrain Your Types to Free Your Brain!Jorge Vásquez
Admit it: You have used a String to model email values, even though most strings aren’t valid emails (don’t worry, we all have!). Imprecise data models are easy, but they crash applications and corrupt external systems. On the other hand, precise data models take time and generate boilerplate.
So-called newtype libraries have stepped up to the challenge, making it easier to model data precisely using runtime validation. However, newtype libraries aren’t able to validate constants at compile-time, and they don’t generally work with Scala 3.
Enter ZIO Prelude Smart Types, which make it simple to model data types precisely, without any boilerplate, runtime overhead, or compile-time overhead. ZIO Prelude Smart Types work at compile-time and runtime, and they have a uniform API across Scala 2 & 3.
Join ZIO Prelude contributor Jorge Vásquez as he teaches you how to be smart, and constrain your types to free your brain!
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidPhilip Schwarz
The subject of this deck is the small Print[A] program in the following blog post by Noel Welsh: https://www.inner-product.com/posts/direct-style-effects/.
Keywords: "direct-style", "context function", "context functions", "algebraic effect", "algebraic effects", "scala", "effect system", "effect systems", "effect", "side effect", "composition", "fp", "functional programming"
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
For functions that can be defined both as an instance of a right fold and as an instance of a left fold, one may be more efficient than the other.
Let's look at the example of a function 'decimal' that converts a list of digits into the corresponding decimal number.
Erratum: it has been pointed out that it is possible to define the zip function using a right fold (see slide 5).
Tagless Final Encoding - Algebras and Interpreters and also ProgramsPhilip Schwarz
Tagless Final Encoding - Algebras and Interpreters and also Programs - An introduction, through the work of Gabriel Volpe.
Slide deck home: http://fpilluminated.com/assets/tagless-final-encoding-algebras-interpreters-and-programs.html
A sighting of traverseFilter and foldMap in Practical FP in ScalaPhilip Schwarz
Slide deck home: http://fpilluminated.com/assets/sighting-of-scala-cats-traverseFilter-and-foldMap-in-practical-fp-in-scala.html.
Download PDF for perfect image quality.
A sighting of sequence function in Practical FP in ScalaPhilip Schwarz
Slide deck home: http://fpilluminated.com/assets/sighting-of-scala-cats-sequence-function-in-practical-fp-in-scala.html.
Download PDF for perfect image quality.
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Philip Schwarz
Kleisli composition, flatMap, join, map, unit. A study/memory aid, to help learn/recall their implementation/interrelation.
Version 2, updated for Scala 3
Nat, List and Option Monoids -from scratch -Combining and Folding -an examplePhilip Schwarz
Nat, List and Option Monoids, from scratch. Combining and Folding: an example.
This is a new version of the original which has some cosmetic changes and a new 7th slide which only differs from slide 6 in that it defines the fold function in terms of the foldRight function.
Code: https://github.com/philipschwarz/nat-list-and-option-monoids-from-scratch-combining-and-folding-an-example
Nat, List and Option Monoids -from scratch -Combining and Folding -an examplePhilip Schwarz
Nat, List and Option Monoids, from scratch. Combining and Folding: an example.
Code: https://github.com/philipschwarz/nat-list-and-option-monoids-from-scratch-combining-and-folding-an-example
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...Philip Schwarz
When I posted the deck for Part 1 to the Scala users forum, Odd Möller linked to a paper titled "The Genuine Sieve of Eratosthenes", which speaks of the Unfaithful Sieve.
Part 2 is based on that paper and on Richard Bird's faithful Haskell implementation of the Sieve, which we translate into Scala.
Scala code for Richard Bird's infinite primes Haskell program: https://github.com/philipschwarz/sieve-of-eratosthenes-part-2-scala
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Philip Schwarz
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Haskell, Scala and Java.
Inspired by the example in Scott Wlaschin’s F# book: Domain Modeling Made Functional.
Download for better results.
Java 19 Code: https://github.com/philipschwarz/fruit-salad-and-fruit-snack-ADT-example-java
Jordan Peterson - The pursuit of meaning and related ethical axiomsPhilip Schwarz
I have only recently become aware of the work of Jordan Peterson. Because I am finding it so interesting, I hope that the following small collection of excerpts from some of his writings and speeches might entice any fellow latecomers to find out more about his work. See below for my own summary of some of the subjects touched upon in these slides.
Download for best results.
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...Philip Schwarz
Defining filter using
(a) recursion
(b) folding
(c) folding with S, B and I combinators
(d) folding with applicative functor and identity function.pdf
This second version adds a simpler folding definition, which I had left out in the first version.
Experience our free, in-depth three-part Tendenci Platform Corporate Membership Management workshop series! In Session 1 on May 14th, 2024, we began with an Introduction and Setup, mastering the configuration of your Corporate Membership Module settings to establish membership types, applications, and more. Then, on May 16th, 2024, in Session 2, we focused on binding individual members to a Corporate Membership and Corporate Reps, teaching you how to add individual members and assign Corporate Representatives to manage dues, renewals, and associated members. Finally, on May 28th, 2024, in Session 3, we covered questions and concerns, addressing any queries or issues you may have.
For more Tendenci AMS events, check out www.tendenci.com/events
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...Juraj Vysvader
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I didn't get rich from it but it did have 63K downloads (powered possible tens of thousands of websites).
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...informapgpstrackings
Keep tabs on your field staff effortlessly with Informap Technology Centre LLC. Real-time tracking, task assignment, and smart features for efficient management. Request a live demo today!
For more details, visit us : https://informapuae.com/field-staff-tracking/
Quarkus Hidden and Forbidden ExtensionsMax Andersen
Quarkus has a vast extension ecosystem and is known for its subsonic and subatomic feature set. Some of these features are not as well known, and some extensions are less talked about, but that does not make them less interesting - quite the opposite.
Come join this talk to see some tips and tricks for using Quarkus and some of the lesser known features, extensions and development techniques.
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Globus
The U.S. Geological Survey (USGS) has made substantial investments in meeting evolving scientific, technical, and policy driven demands on storing, managing, and delivering data. As these demands continue to grow in complexity and scale, the USGS must continue to explore innovative solutions to improve its management, curation, sharing, delivering, and preservation approaches for large-scale research data. Supporting these needs, the USGS has partnered with the University of Chicago-Globus to research and develop advanced repository components and workflows leveraging its current investment in Globus. The primary outcome of this partnership includes the development of a prototype enterprise repository, driven by USGS Data Release requirements, through exploration and implementation of the entire suite of the Globus platform offerings, including Globus Flow, Globus Auth, Globus Transfer, and Globus Search. This presentation will provide insights into this research partnership, introduce the unique requirements and challenges being addressed and provide relevant project progress.
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Globus
The Earth System Grid Federation (ESGF) is a global network of data servers that archives and distributes the planet’s largest collection of Earth system model output for thousands of climate and environmental scientists worldwide. Many of these petabyte-scale data archives are located in proximity to large high-performance computing (HPC) or cloud computing resources, but the primary workflow for data users consists of transferring data, and applying computations on a different system. As a part of the ESGF 2.0 US project (funded by the United States Department of Energy Office of Science), we developed pre-defined data workflows, which can be run on-demand, capable of applying many data reduction and data analysis to the large ESGF data archives, transferring only the resultant analysis (ex. visualizations, smaller data files). In this talk, we will showcase a few of these workflows, highlighting how Globus Flows can be used for petabyte-scale climate analysis.
Why React Native as a Strategic Advantage for Startup Innovation.pdfayushiqss
Do you know that React Native is being increasingly adopted by startups as well as big companies in the mobile app development industry? Big names like Facebook, Instagram, and Pinterest have already integrated this robust open-source framework.
In fact, according to a report by Statista, the number of React Native developers has been steadily increasing over the years, reaching an estimated 1.9 million by the end of 2024. This means that the demand for this framework in the job market has been growing making it a valuable skill.
But what makes React Native so popular for mobile application development? It offers excellent cross-platform capabilities among other benefits. This way, with React Native, developers can write code once and run it on both iOS and Android devices thus saving time and resources leading to shorter development cycles hence faster time-to-market for your app.
Let’s take the example of a startup, which wanted to release their app on both iOS and Android at once. Through the use of React Native they managed to create an app and bring it into the market within a very short period. This helped them gain an advantage over their competitors because they had access to a large user base who were able to generate revenue quickly for them.
Cyaniclab : Software Development Agency Portfolio.pdfCyanic lab
CyanicLab, an offshore custom software development company based in Sweden,India, Finland, is your go-to partner for startup development and innovative web design solutions. Our expert team specializes in crafting cutting-edge software tailored to meet the unique needs of startups and established enterprises alike. From conceptualization to execution, we offer comprehensive services including web and mobile app development, UI/UX design, and ongoing software maintenance. Ready to elevate your business? Contact CyanicLab today and let us propel your vision to success with our top-notch IT solutions.
We describe the deployment and use of Globus Compute for remote computation. This content is aimed at researchers who wish to compute on remote resources using a unified programming interface, as well as system administrators who will deploy and operate Globus Compute services on their research computing infrastructure.
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus
As part of the DOE Integrated Research Infrastructure (IRI) program, NERSC at Lawrence Berkeley National Lab and ALCF at Argonne National Lab are working closely with General Atomics on accelerating the computing requirements of the DIII-D experiment. As part of the work the team is investigating ways to speedup the time to solution for many different parts of the DIII-D workflow including how they run jobs on HPC systems. One of these routes is looking at Globus Compute as a way to replace the current method for managing tasks and we describe a brief proof of concept showing how Globus Compute could help to schedule jobs and be a tool to connect compute at different facilities.
Understanding Globus Data Transfers with NetSageGlobus
NetSage is an open privacy-aware network measurement, analysis, and visualization service designed to help end-users visualize and reason about large data transfers. NetSage traditionally has used a combination of passive measurements, including SNMP and flow data, as well as active measurements, mainly perfSONAR, to provide longitudinal network performance data visualization. It has been deployed by dozens of networks world wide, and is supported domestically by the Engagement and Performance Operations Center (EPOC), NSF #2328479. We have recently expanded the NetSage data sources to include logs for Globus data transfers, following the same privacy-preserving approach as for Flow data. Using the logs for the Texas Advanced Computing Center (TACC) as an example, this talk will walk through several different example use cases that NetSage can answer, including: Who is using Globus to share data with my institution, and what kind of performance are they able to achieve? How many transfers has Globus supported for us? Which sites are we sharing the most data with, and how is that changing over time? How is my site using Globus to move data internally, and what kind of performance do we see for those transfers? What percentage of data transfers at my institution used Globus, and how did the overall data transfer performance compare to the Globus users?
How Recreation Management Software Can Streamline Your Operations.pptxwottaspaceseo
Recreation management software streamlines operations by automating key tasks such as scheduling, registration, and payment processing, reducing manual workload and errors. It provides centralized management of facilities, classes, and events, ensuring efficient resource allocation and facility usage. The software offers user-friendly online portals for easy access to bookings and program information, enhancing customer experience. Real-time reporting and data analytics deliver insights into attendance and preferences, aiding in strategic decision-making. Additionally, effective communication tools keep participants and staff informed with timely updates. Overall, recreation management software enhances efficiency, improves service delivery, and boosts customer satisfaction.
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar
The European Union Agency for Law Enforcement Cooperation (Europol) has suffered an alleged data breach after a notorious threat actor claimed to have exfiltrated data from its systems. Infamous data leaker IntelBroker posted on the even more infamous BreachForums hacking forum, saying that Europol suffered a data breach this month.
The alleged breach affected Europol agencies CCSE, EC3, Europol Platform for Experts, Law Enforcement Forum, and SIRIUS. Infiltration of these entities can disrupt ongoing investigations and compromise sensitive intelligence shared among international law enforcement agencies.
However, this is neither the first nor the last activity of IntekBroker. We have compiled for you what happened in the last few days. To track such hacker activities on dark web sources like hacker forums, private Telegram channels, and other hidden platforms where cyber threats often originate, you can check SOCRadar’s Dark Web News.
Stay Informed on Threat Actors’ Activity on the Dark Web with SOCRadar!
Advanced Flow Concepts Every Developer Should KnowPeter Caitens
Tim Combridge from Sensible Giraffe and Salesforce Ben presents some important tips that all developers should know when dealing with Flows in Salesforce.
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Anthony Dahanne
Les Buildpacks existent depuis plus de 10 ans ! D’abord, ils étaient utilisés pour détecter et construire une application avant de la déployer sur certains PaaS. Ensuite, nous avons pu créer des images Docker (OCI) avec leur dernière génération, les Cloud Native Buildpacks (CNCF en incubation). Sont-ils une bonne alternative au Dockerfile ? Que sont les buildpacks Paketo ? Quelles communautés les soutiennent et comment ?
Venez le découvrir lors de cette session ignite
Globus Connect Server Deep Dive - GlobusWorld 2024Globus
We explore the Globus Connect Server (GCS) architecture and experiment with advanced configuration options and use cases. This content is targeted at system administrators who are familiar with GCS and currently operate—or are planning to operate—broader deployments at their institution.
4. An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
4
5. An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
5
6. An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
6
7. An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
Given a particular placement of 8 queens, how many ways are there of
arriving at it? We pick one cell at a time, in sequence, so how many
different possible sequences are there for picking 8 cells?
7
8. An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
Given a particular placement of 8 queens, how many ways are there of
arriving at it? We pick one cell at a time, in sequence, so how many
different possible sequences are there for picking 8 cells?
For our first pick, we have 8 choices.
7 for the 2nd
6 for the 3rd
5 for the 4th
4 for the 5th
3 for the 6th
2 for the 7th
1 for the 8th
8
9. An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
Given a particular placement of 8 queens, how many ways are there of
arriving at it? We pick one cell at a time, in sequence, so how many
different possible sequences are there for picking 8 cells?
For our first pick, we have 8 choices.
7 for the 2nd
6 for the 3rd
5 for the 4th
4 for the 5th
3 for the 6th
2 for the 7th
1 for the 8th
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320
9
10. So the number of candidate board configurations for the puzzle is
Number of ways of placing 8 queens on the board
Number of ways of arriving at each configuraPon
An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
Given a particular placement of 8 queens, how many ways are there of
arriving at it? We pick one cell at a time, in sequence, so how many
different possible sequences are there for picking 8 cells?
For our first pick, we have 8 choices.
7 for the 2nd
6 for the 3rd
5 for the 4th
4 for the 5th
3 for the 6th
2 for the 7th
1 for the 8th
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320
10
11. So the number of candidate board configurations for the puzzle is
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1
= 4,426,165,368
Number of ways of placing 8 queens on the board
Number of ways of arriving at each configuraPon
An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
Given a particular placement of 8 queens, how many ways are there of
arriving at it? We pick one cell at a time, in sequence, so how many
different possible sequences are there for picking 8 cells?
For our first pick, we have 8 choices.
7 for the 2nd
6 for the 3rd
5 for the 4th
4 for the 5th
3 for the 6th
2 for the 7th
1 for the 8th
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320
11
12. So the number of candidate board configurations for the puzzle is
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1
= 4,426,165,368
Number of ways of placing 8 queens on the board
Number of ways of arriving at each configuraPon
An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
Given a particular placement of 8 queens, how many ways are there of
arriving at it? We pick one cell at a time, in sequence, so how many
different possible sequences are there for picking 8 cells?
For our first pick, we have 8 choices.
7 for the 2nd
6 for the 3rd
5 for the 4th
4 for the 5th
3 for the 6th
2 for the 7th
1 for the 8th
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320
We know that no more than one queen is allowed on each row, since
multiple queens on the same row hold each other in check.
We can use that fact to drastically reduce the number of candidate boards.
12
13. So the number of candidate board configurations for the puzzle is
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1
= 4,426,165,368
Number of ways of placing 8 queens on the board
Number of ways of arriving at each configuraPon
An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
Given a particular placement of 8 queens, how many ways are there of
arriving at it? We pick one cell at a time, in sequence, so how many
different possible sequences are there for picking 8 cells?
For our first pick, we have 8 choices.
7 for the 2nd
6 for the 3rd
5 for the 4th
4 for the 5th
3 for the 6th
2 for the 7th
1 for the 8th
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320
We know that no more than one queen is allowed on each row, since
multiple queens on the same row hold each other in check.
We can use that fact to drastically reduce the number of candidate boards.
To do that, instead of picking 8 cells on the whole board, we consider each
of 8 rows in turn, and on each such row, we pick one of 8 columns.
8 × 8 × 8 × 8 × 8 × 8 × 8 × 8 = 88 = 16,777,216
13
14. We know that no more than one queen is allowed on each row, since
multiple queens on the same row hold each other in check.
We can use that fact to drastically reduce the number of candidate boards.
To do that, instead of picking 8 cells on the whole board, we consider each
of 8 rows in turn, and on each such row, we pick one of 8 columns.
8 × 8 × 8 × 8 × 8 × 8 × 8 × 8 = 88 = 16,777,216
An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
Given a particular placement of 8 queens, how many ways are there of
arriving at it? We pick one cell at a time, in sequence, so how many
different possible sequences are there for picking 8 cells?
For our first pick, we have 8 choices.
7 for the 2nd
6 for the 3rd
5 for the 4th
4 for the 5th
3 for the 6th
2 for the 7th
1 for the 8th
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320
We also know that no more than one queen is allowed on each column, so
we can again reduce the number of candidate boards.
14
So the number of candidate board configurations for the puzzle is
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1
= 4,426,165,368
Number of ways of placing 8 queens on the board
Number of ways of arriving at each configuraPon
15. So the number of candidate board configurations for the puzzle is
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1
= 4,426,165,368
Number of ways of placing 8 queens on the board
Number of ways of arriving at each configuraPon
We also know that no more than one queen is allowed on each column, so
we can again reduce the number of candidate boards.
If we pick a column on one row, we cannot pick it again on subsequent
rows, i.e. the choice of columns that we can pick decreases as we progress
through the rows.
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320
An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
For the 1st queen, we have a choice of 64 cells
63 for the 2nd queen
62 for the 3rd
61 for the 4th
60 for the 5th
59 for the 6th
58 for the 7th
57 for the 8th
64 × 63 × 62 × 61 × 60 × 59 × 58 × 57 = 178,462,987,637,760
Given a particular placement of 8 queens, how many ways are there of
arriving at it? We pick one cell at a time, in sequence, so how many
different possible sequences are there for picking 8 cells?
For our first pick, we have 8 choices.
7 for the 2nd
6 for the 3rd
5 for the 4th
4 for the 5th
3 for the 6th
2 for the 7th
1 for the 8th
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320
We know that no more than one queen is allowed on each row, since
multiple queens on the same row hold each other in check.
We can use that fact to drastically reduce the number of candidate boards.
To do that, instead of picking 8 cells on the whole board, we consider each
of 8 rows in turn, and on each such row, we pick one of 8 columns.
8 × 8 × 8 × 8 × 8 × 8 × 8 × 8 = 88 = 16,777,216
15
35. A particularly suitable application area of for expressions are
combinatorial puzzles.
An example of such a puzzle is the 8-queens problem: Given a
standard chess-board, place eight queens such that no queen
is in check from any other (a queen can check another piece
if they are on the same column, row, or diagonal).
Martin Odersky
35
46. val redSquare = Image.square(100).fillColor(Color.red)
val blueSquare = Image.square(100).fillColor(Color.blue)
46
47. val redSquare = Image.square(100).fillColor(Color.red)
val blueSquare = Image.square(100).fillColor(Color.blue)
val redBesideBlue = redSquare.beside(blueSquare)
47
48. val redSquare = Image.square(100).fillColor(Color.red)
val blueSquare = Image.square(100).fillColor(Color.blue)
val redBesideBlue = redSquare.beside(blueSquare)
val redAboveBlue = redSquare.above(blueSquare)
48
56. def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
reduce
m
a
p
reduce
reduce
reduce
r
e
d
u
c
e
56
above
beside
58. def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.fold(Image.empty)(_ beside _))
.fold(Image.empty)(_ above _)
safer to use fold, in case any of the lists is empty
58
59. def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.fold(Image.empty)(_ beside _))
.fold(Image.empty)(_ above _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.foldLeft(Image.empty)(_ beside _))
.foldLeft(Image.empty)(_ above _)
safer to use fold, in case any of the lists is empty
fold is just an alias for foldLeft
59
60. trait Foldable[F[_]] {
def foldMap[A, B](as: F[A])(f: A => B)(mb: Monoid[B]): B =
foldRight(as)(mb.zero)((a, b) => mb.op(f(a), b))
def foldRight[A, B](as: F[A])(z: B)(f: (A, B) => B): B =
foldMap(as)(f.curried)(endoMonoid[B])(z)
def concatenate[A](as: F[A])(m: Monoid[A]): A =
foldLeft(as)(m.zero)(m.op)
def foldLeft[A, B](as: F[A])(z: B)(f: (B, A) => B): B =
foldMap(as)(a => (b: B) => f(b, a))(dual(endoMonoid[B]))(z)
def toList[A](as: F[A]): List[A] =
foldRight(as)(List[A]())(_ :: _)
}
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
60
61. trait Foldable[F[_]] {
def foldMap[A, B](as: F[A])(f: A => B)(mb: Monoid[B]): B =
foldRight(as)(mb.zero)((a, b) => mb.op(f(a), b))
def foldRight[A, B](as: F[A])(z: B)(f: (A, B) => B): B =
foldMap(as)(f.curried)(endoMonoid[B])(z)
def concatenate[A](as: F[A])(m: Monoid[A]): A =
foldLeft(as)(m.zero)(m.op)
def foldLeft[A, B](as: F[A])(z: B)(f: (B, A) => B): B =
foldMap(as)(a => (b: B) => f(b, a))(dual(endoMonoid[B]))(z)
def toList[A](as: F[A]): List[A] =
foldRight(as)(List[A]())(_ :: _)
}
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
concatenate fold,combineAll
foldMap foldMap
foldLeft foldLeft
foldRight foldRight
61
78. trait Foldable[F[_]] {
def foldMap[A, B](as: F[A])(f: A => B)(mb: Monoid[B]): B =
foldRight(as)(mb.zero)((a, b) => mb.op(f(a), b))
def foldRight[A, B](as: F[A])(z: B)(f: (A, B) => B): B =
foldMap(as)(f.curried)(endoMonoid[B])(z)
def concatenate[A](as: F[A])(m: Monoid[A]): A =
foldLeft(as)(m.zero)(m.op)
def foldLeft[A, B](as: F[A])(z: B)(f: (B, A) => B): B =
foldMap(as)(a => (b: B) => f(b, a))(dual(endoMonoid[B]))(z)
def toList[A](as: F[A]): List[A] =
foldRight(as)(List[A]())(_ :: _)
}
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
concatenate fold,combineAll
foldMap foldMap
foldLeft foldLeft
foldRight foldRight
The marketing buzzword for foldMap is MapReduce.
78
79. object ListFoldable extends Foldable[List] {
override def foldMap[A, B](as:List[A])(f: A => B)(mb: Monoid[B]): B =
foldLeft(as)(mb.zero)((b, a) => mb.op(b, f(a)))
override def foldRight[A, B](as: List[A])(z: B)(f: (A,B) => B) =
as match {
case Nil => z
case Cons(h, t) => f(h, foldRight(t, z)(f))
}
override def foldLeft[A, B](as: List[A])(z: B)(f: (B,A) => B) =
as match {
case Nil => z
case Cons(h, t) => foldLeft(t, f(z,h))(f)
}
}
79
81. def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.combineAll(beside))
.foldLeft(Image.empty)(_ above _)
purely to make the next step easier to understand,
let’s revert to using reduce rather than foldLeft.
81
82. def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.combineAll(beside))
.foldLeft(Image.empty)(_ above _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.combineAll(beside))
.reduce(_ above _)
purely to make the next step easier to understand,
let’s revert to using reduce rather than foldLeft.
82
93. 93
The images that we have been creating up to now have been centered on the origin of the coordinate system
94. val (x,y) = …
squareImage = squareImageAtOrigin.at(x,y)
val (x,y) = …
boardImage = boardImageAtOrigin.at(x,y)
94
But we can use an image’s at function to specify a desired position for the image
And if we are prepared to do that, we can further simplify the way we combine images
101. given Monoid.instance[Image] = Monoid.instance[Image](Image.empty, _ on _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
FULL RECAP
101
102. 8 × 8 × 8 × 8 × 8 × 8 × 8 × 8 = 88 = 16,777,216 i.e. # of permutations of 8 queens (repetition allowed)
Earlier we looked at the number of ways of placing 8 queens on the board, one queen per row
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320 “ “ “ “ “ without repetition
102
103. Earlier we looked at the number of ways of placing 8 queens on the board, one queen per row
So the formulas for arbitrary N are the following:
𝑁𝑁 # of permutations of N queens (repetition allowed)
𝑁! # of permutations of N queens without repetition
8 × 8 × 8 × 8 × 8 × 8 × 8 × 8 = 88 = 16,777,216 i.e. # of permutations of 8 queens (repetition allowed)
8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 8! = 40,320 “ “ “ “ “ without repetition
103
104. How do we compute the permutations of N queens, e.g. for N=4?
104
105. def permutations(): List[List[Int]] =
{ for
firstQueen <- 1 to 4
secondQueen <- 1 to 4
thirdQueen <- 1 to 4
fourthQueen <- 1 to 4
queens = List(firstQueen, secondQueen, thirdQueen, fourthQueen)
yield queens
}.toList
105
113. A full solution can not be found in a single step.
It needs to be built up gradually, by occupying successive rows with queens.
This suggests a recursive algorithm.
Martin Odersky
113
114. Martin Odersky
Assume you have already generated all solutions of placing k
queens on a board of size N x N, where k is less than N.
…
Now, to place the next queen in row k + 1, generate all possible
extensions of each previous solution by one more queen.
114
115. Martin Odersky
Assume you have already generated all solutions of placing k
queens on a board of size N x N, where k is less than N.
…
Now, to place the next queen in row k + 1, generate all possible
extensions of each previous solution by one more queen.
This yields another list of solutions lists, this time of length k + 1.
Continue the process until you have obtained all solutions of the
size of the chess-board N.
115
116. def permutations(): List[List[Int]] =
{ for
firstQueen <- 1 to 4
secondQueen <- 1 to 4
thirdQueen <- 1 to 4
fourthQueen <- 1 to 4
queens = List(firstQueen, secondQueen, thirdQueen, fourthQueen)
yield queens
}.toList
116
117. def permutations(n:Int = 4): List[List[Int]] =
if n == 0 then List(List())
else
for
queens <- permutations(n-1)
queen <- 1 to 4
yield queen :: queens
def permutations(): List[List[Int]] =
{ for
firstQueen <- 1 to 4
secondQueen <- 1 to 4
thirdQueen <- 1 to 4
fourthQueen <- 1 to 4
queens = List(firstQueen, secondQueen, thirdQueen, fourthQueen)
yield queens
}.toList
simplify by using recursion
117
118. def permutations(n:Int = 4): List[List[Int]] =
if n == 0 then List(List())
else
for
queens <- permutations(n-1)
queen <- 1 to 4
yield queen :: queens
def permutations(): List[List[Int]] =
{ for
firstQueen <- 1 to 4
secondQueen <- 1 to 4
thirdQueen <- 1 to 4
fourthQueen <- 1 to 4
queens = List(firstQueen, secondQueen, thirdQueen, fourthQueen)
yield queens
}.toList
simplify by using recursion
118
119. def permutations(n:Int = 4): List[List[Int]] =
if n == 0 then List(List())
else
for
queens <- permutations(n-1)
queen <- 1 to 4
yield queen :: queens
def permutations(): List[List[Int]] =
{ for
firstQueen <- 1 to 4
secondQueen <- 1 to 4
thirdQueen <- 1 to 4
fourthQueen <- 1 to 4
queens = List(firstQueen, secondQueen, thirdQueen, fourthQueen)
yield queens
}.toList
simplify by using recursion
119
120. def permutations(n:Int = 4): List[List[Int]] =
if n == 0 then List(List())
else
for
queens <- permutations(n-1)
queen <- 1 to 4
yield queen :: queens
def permutations(): List[List[Int]] =
{ for
firstQueen <- 1 to 4
secondQueen <- 1 to 4
thirdQueen <- 1 to 4
fourthQueen <- 1 to 4
queens = List(firstQueen, secondQueen, thirdQueen, fourthQueen)
yield queens
}.toList
simplify by using recursion
120
121. def permutations(n:Int = 4): List[List[Int]] =
if n == 0 then List(List())
else
for
queens <- permutations(n-1)
queen <- 1 to 4
yield queen :: queens
def permutations(): List[List[Int]] =
{ for
firstQueen <- 1 to 4
secondQueen <- 1 to 4
thirdQueen <- 1 to 4
fourthQueen <- 1 to 4
queens = List(firstQueen, secondQueen, thirdQueen, fourthQueen)
yield queens
}.toList
simplify by using recursion
121
122. N = 4
# of permutations = 44 = 256
Same result as before
using recursion
122
123. def queens(n: Int): List[List[(Int,Int)]] = {
def placeQueens(k: Int): List[List[(Int,Int)]] =
if (k == 0)
List(List())
else
for {
queens <- placeQueens(k - 1)
column <- 1 to n
queen = (k, column)
if isSafe(queen, queens)
} yield queen :: queens
placeQueens(n)
}
def queens(n: Int): Set[List[Int]] = {
def placeQueens(k: Int): Set[List[Int]] =
if (k == 0)
Set(List())
else
for {
queens <- placeQueens(k - 1)
col <- 0 until n
if isSafe(col, queens)
} yield col :: queens
placeQueens(n)
}
def isSafe(queen: (Int, Int), queens: List[(Int, Int)]) =
queens forall (q => !inCheck(queen, q))
def inCheck(q1: (Int, Int), q2: (Int, Int)) =
q1._1 == q2._1 || // same row
q1._2 == q2._2 || // same column
(q1._1 - q2._1).abs == (q1._2 - q2._2).abs // on diagonal
def isSafe(col: Int, queens: List[Int]): Boolean = {
val row = queens.length
val queensWithRow = (row - 1 to 0 by -1) zip queens
queensWithRow forall
{ case (r, c) => col != c && math.abs(col - c) != row - r }
}
Martin Odersky
This algorithmic idea is
embodied in function
placeQueens.
123
124. def queens(n: Int): List[List[(Int,Int)]] = {
def placeQueens(k: Int): List[List[(Int,Int)]] =
if (k == 0) List(List())
else
for {
queens <- placeQueens(k - 1)
column <- 1 to n
queen = (k, column)
if isSafe(queen, queens)
} yield queen :: queens
placeQueens(n)
}
def permutations(n:Int = 4): List[List[Int]] =
if n == 0 then List(List())
else
for
queens <- permutations(n-1)
queen <- 1 to 4
yield queen :: queens
124
125. The only remaining bit is the isSafe method, which is used to check whether a given queen is in check from any other element
in a list of queens.
Here is the definition:
The isSafe method expresses that a queen is safe with respect to some other queens if it is not in check from any other queen.
The inCheck method expresses that queens q1 and q2 are mutually in check.
It returns true in one of three cases:
1. If the two queens have the same row coordinate.
2. If the two queens have the same column coordinate.
3. If the two queens are on the same diagonal (i.e., the difference between their rows and the difference between their
columns are the same).
The first case – that the two queens have the same row coordinate – cannot happen in the application because placeQueens
already takes care to place each queen in a different row. So you could remove the test without changing the functionality of the
program.
def isSafe(queen: (Int, Int), queens: List[(Int, Int)]) =
queens forall (q => !inCheck(queen, q))
def inCheck(q1: (Int, Int), q2: (Int, Int)) =
q1._1 == q2._1 || // same row
q1._2 == q2._2 || // same column
(q1._1 - q2._1).abs == (q1._2 - q2._2).abs // on diagonal
Martin Odersky
125
126. def permutations(n:Int = 4): List[List[Int]] =
if n == 0 then List(List())
else
for
queens <- permutations(n-1)
queen <- 1 to 4
yield queen :: queens
126
127. def permutations(n:Int = 4): List[List[Int]] =
if n == 0 then List(List())
else
for
queens <- permutations(n-1)
queen <- 1 to 4
if isSafe(queen,queens)
yield queen :: queens
use isSafe function
def permutations(n:Int = 4): List[List[Int]] =
if n == 0 then List(List())
else
for
queens <- permutations(n-1)
queen <- 1 to 4
yield queen :: queens
127
141. 92 boards
N = 5
N = 6
N = 4
N = 7
N = 8
40 boards
4 boards
10 boards
2 boards
142. queens n = placeQueens n
where
placeQueens 0 = [[]]
placeQueens k = [queen:queens |
queens <- placeQueens(k-1),
queen <- [1..n],
safe queen queens]
safe queen queens = all safe (zipWithRows queens)
where
safe (r,c) = c /= col && not (onDiagonal col row c r)
row = length queens
col = queen
onDiagonal row column otherRow otherColumn =
abs (row - otherRow) == abs (column - otherColumn)
zipWithRows queens = zip rowNumbers queens
where
rowCount = length queens
rowNumbers = [rowCount-1,rowCount-2..0]
def queens(n: Int): List[List[Int]] =
def placeQueens(k: Int): List[List[Int]] =
if k == 0
then List(List())
else
for
queens <- placeQueens(k - 1)
queen <- 1 to n
if safe(queen, queens)
yield queen :: queens
placeQueens(n)
def onDiagonal(row: Int, column: Int, otherRow: Int, otherColumn: Int) =
math.abs(row - otherRow) == math.abs(column - otherColumn)
def safe(queen: Int, queens: List[Int]): Boolean =
val (row, column) = (queens.length, queen)
val safe: ((Int,Int)) => Boolean = (nextRow, nextColumn) =>
column != nextColumn && !onDiagonal(column, row, nextColumn, nextRow)
zipWithRows(queens) forall safe
def zipWithRows(queens: List[Int]): Iterable[(Int,Int)] =
val rowCount = queens.length
val rowNumbers = rowCount - 1 to 0 by -1
rowNumbers zip queens
Recursive Algorithm
142
143. https://rosettacode.org/wiki/N-queens_problem#Haskell
On the Rosetta Code site, I came across a Haskell N-Queens puzzle program which
• does not use recursion
• uses a function called foldM
• it is succinct, but relies on comments to help understand how it works
https://rosettacode.org/wiki/
143
144. In the rest of this talk we shall
• gain an understanding of the foldM function
• see how it can be used to write an iterative solution to the puzzle
144
145. We are all very familiar with the 3 functions that are the bread, butter, and jam of Functional Programming
145
146. We are all very familiar with the 3 functions that are the bread, butter, and jam of Functional Programming
map
λ
I am (of course) referring to the triad of map, filter and fold
146
147. map
λ
mapM
λ
Let’s gain an understanding of the foldM function by looking at the monadic variant of the triad
147
149. Generic functions
An important benefit of abstracting out the concept of monads is the ability to define generic
functions that can be used with any monad.
…
For example, a monadic version of the map function on list can be defined as follows:
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f [] = return []
mapM f (x:xs) = do y <- f x
ys <- mapM f xs
return (y:ys)
Note that mapM has the same type as map, except that the argument function and the function
itself now have monadic return types.
Graham Hutton
@haskellhutt
149
151. mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f [] = return []
mapM f (x:xs) = do y <- f x
ys <- mapM f xs
return (y:ys)
map :: (a -> b) -> [a] -> [b]
151
152. import cats.Monad
import cats.syntax.functor.* // map
import cats.syntax.flatMap.* // flatMap
import cats.syntax.applicative.* // pure
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as =>
for
b <- f(a)
bs <- mapM(as)(f)
yield b::bs
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f [] = return []
mapM f (x:xs) = do y <- f x
ys <- mapM f xs
return (y:ys)
Cats
map :: (a -> b) -> [a] -> [b]
152
156. final def traverse[A, B, M <: (IterabelOnce)]
(in: M[A])
(fn: A => Future[B])
(implicit bf: BuildFrom[M[A], B, M[B]],
executor: ExecutionContext)
: Future[M[B]]
Asynchronously and non-blockingly transforms a IterableOnce[A] into a Future[IterableOnce[B]] using the provided
function A => Future[B].
This is useful for performing a parallel map. For example, to apply a function to all items of a list in parallel.
final def sequence[A, CC <: (IterabelOnce), To]
(in: CC[Future[A]])
(implicit bf: BuildFrom[CC[Future[A]], A, To],
executor: ExecutionContext)
: Future[To]
Simple version of Future.traverse.
156
While there is a traverse function in Scala’s standard library, it is specific to Future, and IterableOnce.
158. scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
// list of integers
scala> val listOfInts = List(1,2,3)
val listOfInts: List[Int] = List(1, 2, 3)
158
159. scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
// list of integers
scala> val listOfInts = List(1,2,3)
val listOfInts: List[Int] = List(1, 2, 3)
// list of future integers
scala> val listOfFutureInts = listOfInts.map{ n => Future(n*10) }
159
160. scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
// list of integers
scala> val listOfInts = List(1,2,3)
val listOfInts: List[Int] = List(1, 2, 3)
// list of future integers
scala> val listOfFutureInts = listOfInts.map{ n => Future(n*10) }
val listOfFutureInts: List[Future[Int]] =
List(Future(Success(10)), Future(Success(20)), Future(Success(30)))
160
161. scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
// list of integers
scala> val listOfInts = List(1,2,3)
val listOfInts: List[Int] = List(1, 2, 3)
// list of future integers
scala> val listOfFutureInts = listOfInts.map{ n => Future(n*10) }
val listOfFutureInts: List[Future[Int]] =
List(Future(Success(10)), Future(Success(20)), Future(Success(30)))
// turn List[Future[Int]] into Future[List[Int]]
// i.e. it turns the nesting of Future within List inside out
scala> val futureListOfInts = Future.sequence(listOfFutureInts)
161
162. scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
// list of integers
scala> val listOfInts = List(1,2,3)
val listOfInts: List[Int] = List(1, 2, 3)
// list of future integers
scala> val listOfFutureInts = listOfInts.map{ n => Future(n*10) }
val listOfFutureInts: List[Future[Int]] =
List(Future(Success(10)), Future(Success(20)), Future(Success(30)))
// turn List[Future[Int]] into Future[List[Int]]
// i.e. it turns the nesting of Future within List inside out
scala> val futureListOfInts = Future.sequence(listOfFutureInts)
val futureListOfInts: Future[List[Int]] =
Future(Success(List(10, 20, 30)))
162
163. scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
// list of integers
scala> val listOfInts = List(1,2,3)
val listOfInts: List[Int] = List(1, 2, 3)
// list of future integers
scala> val listOfFutureInts = listOfInts.map{ n => Future(n*10) }
val listOfFutureInts: List[Future[Int]] =
List(Future(Success(10)), Future(Success(20)), Future(Success(30)))
// turn List[Future[Int]] into Future[List[Int]]
// i.e. it turns the nesting of Future within List inside out
scala> val futureListOfInts = Future.sequence(listOfFutureInts)
val futureListOfInts: Future[List[Int]] =
Future(Success(List(10, 20, 30)))
turn
inside
out
163
164. scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
// list of integers
scala> val listOfInts = List(1,2,3)
val listOfInts: List[Int] = List(1, 2, 3)
// list of future integers
scala> val listOfFutureInts = listOfInts.map{ n => Future(n*10) }
val listOfFutureInts: List[Future[Int]] =
List(Future(Success(10)), Future(Success(20)), Future(Success(30)))
// turn List[Future[Int]] into Future[List[Int]]
// i.e. it turns the nesting of Future within List inside out
scala> val futureListOfInts = Future.sequence(listOfFutureInts)
val futureListOfInts: Future[List[Int]] =
Future(Success(List(10, 20, 30)))
// first map List[Int] to List[Future[Int]]
// and then turn List[Future[Int]] into Future[List[Int]]
scala> val futureListOfInts = Future.traverse(listOfInts) { n => Future(n*10) }
164
165. scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
// list of integers
scala> val listOfInts = List(1,2,3)
val listOfInts: List[Int] = List(1, 2, 3)
// list of future integers
scala> val listOfFutureInts = listOfInts.map{ n => Future(n*10) }
val listOfFutureInts: List[Future[Int]] =
List(Future(Success(10)), Future(Success(20)), Future(Success(30)))
// turn List[Future[Int]] into Future[List[Int]]
// i.e. it turns the nesting of Future within List inside out
scala> val futureListOfInts = Future.sequence(listOfFutureInts)
val futureListOfInts: Future[List[Int]] =
Future(Success(List(10, 20, 30)))
// first map List[Int] to List[Future[Int]]
// and then turn List[Future[Int]] into Future[List[Int]]
scala> val futureListOfInts = Future.traverse(listOfInts) { n => Future(n*10) }
val futureListOfInts: Future[List[Int]] =
Future(Success(List(10, 20, 30)))
165
166. scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
// list of integers
scala> val listOfInts = List(1,2,3)
val listOfInts: List[Int] = List(1, 2, 3)
// list of future integers
scala> val listOfFutureInts = listOfInts.map{ n => Future(n*10) }
val listOfFutureInts: List[Future[Int]] =
List(Future(Success(10)), Future(Success(20)), Future(Success(30)))
// turn List[Future[Int]] into Future[List[Int]]
// i.e. it turns the nesting of Future within List inside out
scala> val futureListOfInts = Future.sequence(listOfFutureInts)
val futureListOfInts: Future[List[Int]] =
Future(Success(List(10, 20, 30)))
// first map List[Int] to List[Future[Int]]
// and then turn List[Future[Int]] into Future[List[Int]]
scala> val futureListOfInts = Future.traverse(listOfInts) { n => Future(n*10) }
val futureListOfInts: Future[List[Int]] =
Future(Success(List(10, 20, 30)))
same
result
166
167. That was the quick refresher on the traverse function
167
168. class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
168
169. mapM :: Monad m => (a -> m b) -> [a] -> m [b]
map :: (a -> b) -> [a] -> [b]
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
169
170. mapM :: Monad m => (a -> m b) -> [a] -> m [b]
map :: (a -> b) -> [a] -> [b]
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
170
171. mapM :: Monad m => (a -> m b) -> [a] -> m [b]
map :: (a -> b) -> [a] -> [b]
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
171
172. mapM :: Monad m => (a -> m b) -> [a] -> m [b]
map :: (a -> b) -> [a] -> [b]
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
…
mapM :: Monad m => (a -> m b) -> t a -> m (t b)
mapM = traverse
172
173. /**
* Traverse, also known as Traversable.
*
* Traversal over a structure with an effect.
*
* Traversing with the [[cats.Id]] effect is equivalent to [[cats.Functor]]#map.
* Traversing with the [[cats.data.Const]] effect where the first type parameter has
* a [[cats.Monoid]] instance is equivalent to [[cats.Foldable]]#fold.
*
* See: [[https://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf The Essence of the Iterator Pattern]]
*/
trait Traverse[F[_]] extends Functor[F] with Foldable[F] with UnorderedTraverse[F] { self =>
/**
* Given a function which returns a G effect, thread this effect
* through the running of this function on all the values in F,
* returning an F[B] in a G context.
def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
…
mapM :: Monad m => (a -> m b) -> t a -> m (t b)
mapM = traverse
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
map :: (a -> b) -> [a] -> [b]
173
174. import cats.{Applicative, Monad}
import cats.syntax.traverse.*
import cats.Traverse
extension[A, B, M[_] : Monad] (as: List[A])
def mapM(f: A => M[B]): M[List[B]] =
as.traverse(f)
def mapM [A, B, M[_]: Monad](as: List[A])(f: A => M[B]): M[List[B]]
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
174
175. trait Traverse[F[_]] extends Functor[F] with Foldable[F] {
…
def traverse[G[_],A,B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[F[B]]
…
}
🤯
175
I am not really surprised
that mapM is just traverse.
As seen in the red book…
176. G = Id Monad
trait Traverse[F[_]] extends Functor[F] with Foldable[F] {
…
def map[A,B](fa: F[A])(f: A => B): F[B] = traverse…
def traverse[G[_],A,B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[F[B]]
…
}
🤯
176
… traverse is so general that it
can be used to define map…
177. G = Id Monad G = Applicative Monoid
trait Traverse[F[_]] extends Functor[F] with Foldable[F] {
…
def map[A,B](fa: F[A])(f: A => B): F[B] = traverse…
def foldMap[A,M](as: F[A])(f: A => M)(mb: Monoid[M]): M = traverse…
def traverse[G[_],A,B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[F[B]]
…
}
🤯
177
… and foldMap
178. A quick mention of the Symmetry that exists in the interrelation of
flatMap/foldMap/traverse and flatten/fold/sequence
178
185. def flatMap[A,B](ma: F[A])(f: A ⇒ F[B]): F[B] = flatten(map(ma)(f))
def foldMap[A,B:Monoid](fa: F[A])(f: A ⇒ B): B = fold(map(fa)(f))
def traverse[M[_]:Applicative,A,B](fa: F[A])(f: A ⇒ M[B]): M[F[B]] = sequence(map(fa)(f))
def flatten[A](mma: F[F[A]]): F[A] = flatMap(mma)(x ⇒ x)
def fold[A:Monoid](fa: F[A]): A = foldMap(fa)(x ⇒ x)
def sequence[M[_]:Applicative,A](fma: F[M[A]]): M[F[A]] = traverse(fma)(x ⇒ x)
Symmetry in the interrelation of flatMap/foldMap/traverse and flatten/fold/sequence
185
186. Examples of mapM usage
Example Monadic Context How the result of mapM is affected
1
2
3
186
187. Examples of mapM usage
Example Monadic Context How the result of mapM is affected
1 Option There may or may not be a result.
2
3
187
188. To illustrate how it might be used, consider a function that converts a digit character to its
numeric value, provided that the character is indeed a digit:
conv :: Char -> Maybe Int
conv c | isDigit c = Just (digitToInt c)
| otherwise = Nothing
(The functions isDigit and digitToInt are provided in Data.Char.) Then applying mapM to the conv
function gives a means of converting a string of digits into the corresponding list of numeric
values, which succeeds if every character in the string is a digit, and fails otherwise:
> mapM conv "1234"
Just [1,2,3,4]
> mapM conv "123a"
Nothing
Graham Hutton
@haskellhutt
def convert(c: Char): Option[Int] =
Option.when(c.isDigit)(c.asDigit)
assert("12a4".toList.mapM(convert) == None)
assert("1234".toList.mapM(convert) == Some(List(1, 2, 3, 4)))
def mapM [A, B, M[_]: Monad](as: List[A])(f: A => M[B]): M[List[B]] M = Option
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
188
189. Let’s look at three examples of mapM usage:
Example Monadic Context How the result of mapM is affected
1 Option There may or may not be a result.
2
3
189
190. Let’s look at three examples of mapM usage:
Example Monadic Context How the result of mapM is affected
1 Option There may or may not be a result.
2 List There may be zero, one, or more results.
3
190
191. • X is combined first with 1, and then with 2, and the
results are paired
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
191
192. • X is combined first with 1, and then with 2, and the
results are paired
List("X", "Y").map{ c => List(c + "1", c + "2") }
X Y
1 2
192
193. List(List("X1","X2"))
• X is combined first with 1, and then with 2, and the
results are paired
List("X", "Y").map{ c => List(c + "1", c + "2") }
X Y
1 2
193
194. List(List("X1","X2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
194
195. List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
195
196. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as =>
for
b <- f(a)
bs <- mapM(as)(f)
yield b::bs 196
M = List
197. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a)
bs <- mapM(as)(f)
yield b::bs 197
198. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2"))
bs <- mapM(as)(f)
yield b::bs 198
199. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2"))
bs <- mapM(as)(f)
yield b::bs 199
200. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2"))
bs <- mapM(as)(f) // ??
yield b::bs 200
201. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a)
bs <- mapM(as)(f)
yield b::bs 201
à recursive call for List("Y")
202. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f)
yield b::bs 202
à recursive call for List("Y")
203. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f)
yield b::bs 203
à recursive call for List("Y")
204. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f) // ??
yield b::bs 204
à recursive call for List("Y")
205. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure // List(List())
case a::as =>
for
b <- f(a)
bs <- mapM(as)(f)
yield b::bs 205
à recursive call for List("Y")
à recursive call for List()
206. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f) // List(List())
yield b::bs 206
à recursive call for List("Y")
à recursive call for List()
207. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f) // List(List())
yield b::bs 207
à recursive call for List("Y")
à recursive call for List()
208. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f) // List(List())
yield b::bs // "Y1"::List() 208
à recursive call for List("Y")
à recursive call for List()
List("Y1")
209. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f) // ??
yield b::bs 209
à recursive call for List("Y")
à recursive call for List()
List("Y1")
210. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure // List(List())
case a::as =>
for
b <- f(a)
bs <- mapM(as)(f)
yield b::bs 210
à recursive call for List("Y")
à recursive call for List()
à recursive call for List()
List("Y1")
211. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f) // List(List())
yield b::bs 211
à recursive call for List("Y")
à recursive call for List()
à recursive call for List()
List("Y1")
212. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f) // List(List())
yield b::bs 212
à recursive call for List("Y")
à recursive call for List()
à recursive call for List()
List("Y1")
213. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "Y"::List()
for
b <- f(a) // List("Y1","Y2")
bs <- mapM(as)(f) // List(List())
yield b::bs // "Y2"::List() 213
à recursive call for List("Y")
à recursive call for List()
à recursive call for List()
List("Y1") List("Y2")
214. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs 214
M = List
à recursive call for List("Y")
à recursive call for List()
à recursive call for List()
215. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs 215
M = List
à recursive call for List("Y")
à recursive call for List()
à recursive call for List()
216. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs // List("X1", "Y1") 216
à recursive call for List("Y")
à recursive call for List()
à recursive call for List()
List(List("X1","Y1"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
217. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs 217
218. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs 218
219. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"), List("X1","Y2"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs // List("X1","Y2") 219
220. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"), List("X1","Y2"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
• X is combined with 2 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs 220
221. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"), List("X1","Y2"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
• X is combined with 2 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs 221
222. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"), List("X1","Y2"),
List("X2","Y1"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
• X is combined with 2 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs // List("X2","Y1") 222
223. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"), List("X1","Y2"),
List("X2","Y1"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
• X is combined with 2 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs 223
224. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"), List("X1","Y2"),
List("X2","Y1"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
• X is combined with 2 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs 224
225. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"), List("X1","Y2"),
List("X2","Y1"), List("X2","Y2"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
• X is combined with 2 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs // List("X2","Y2") 225
X Y
1 2
226. List("X", "Y").mapM{ c => List( c + "1", c + "2") }
List(List("X1","X2"), List("Y1","Y2"))
• X is combined first with 1, and then with 2, and the
results are paired
• Y is combined first with 1, and then with 2, and the
results are paired
List(List("X1","Y1"), List("X1","Y2"),
List("X2","Y1"), List("X2","Y2"))
• X is combined with 1 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
• X is combined with 2 and paired, first with the result of combining Y
with 1, and then with the result of combining Y with 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
X Y
1 2
List("X", "Y").map{ c => List(c + "1", c + "2") }
def mapM[A, B, M[_]: Monad](l: List[A])(f: A => M[B]): M[List[B]] = l match
case Nil => Nil.pure
case a::as => // "X"::List("Y")
for
b <- f(a) // List("X1","X2")
bs <- mapM(as)(f) // List(List("Y1"),List("Y2"))
yield b::bs // List("X2","Y2") 226
X Y
1 2
227. Examples of mapM usage
Example Monadic Context How the result of mapM is affected
1 Option There may or may not be a result.
2 List There may be zero, one, or more results.
3
227
228. Examples of mapM usage
Example Monadic Context How the result of mapM is affected
1 Option There may or may not be a result.
2 List There may be zero, one, or more results.
3 IO The result suspends any side effects.
228