N-Queens Combinatorial Puzzle meets Cats

Philip Schwarz
Philip SchwarzSoftware development is what I am passionate about
β€’β€’
π‘šπ‘Žπ‘π‘€
monadic mapping, filtering, folding π‘“π‘–π‘™π‘‘π‘’π‘Ÿπ‘€
π‘“π‘œπ‘™π‘‘π‘€
N-Queens Combinatorial Puzzle meetsCats
monoidal functions π‘“π‘œπ‘™π‘‘ and π‘“π‘œπ‘™π‘‘π‘€π‘Žπ‘
2
https://speakerdeck.com/philipschwarz
https://fosstodon.org/@philip_schwarz
https://www.slideshare.net/pjschwarz
https://twitter.com/philip_schwarz
If you decide you like this, you can find more content like it on http://fpilluminated.com
https://github.com/philipschwarz https://www.linkedin.com/in/philip-schwarz-70576a17/
Some of the other places
where you can find me
3
An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8
queens on the board?
4
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
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
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
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
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
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
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
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
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
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
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
Let’s try to find a solution to the puzzle
16
17
18
19
20
21
22
23
We are stuck. There is nowhere to
place queen number 6, because
every empty cell on the board is in
check from queens 1 to 5.
Let’s try again
24
25
26
27
28
29
30
31
32
33
34
We found a solution
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
36
(7, 3)
(8, 6)
(6, 7)
(5, 2)
(4, 8)
(3, 5)
(2, 1)
(1, 4)
R C
O O
W L
List((8, 6), (7, 3), (6, 7), (5, 2), (4, 8), (3, 5), (2, 1), (1, 4))
37
(6, 2)
(7, 5)
(5, 6)
(4, 1)
(3, 7)
(2, 4)
(1, 0)
(0, 3)
R C
O O
W L
List(5, 2, 6, 1, 7, 4, 0, 3)
38
39
def show(queens: List[Int]): String =
val lines: List[String] =
for (col <- queens.reverse)
yield Vector.fill(queens.length)("❎ ")
.updated(col, s"πŸ‘‘ ")
.mkString
"n" + lines.mkString("n")
40
def show(queens: List[Int]): String =
val lines: List[String] =
for (col <- queens.reverse)
yield Vector.fill(queens.length)("❎ ")
.updated(col, s"πŸ‘‘ ")
.mkString
"n" + lines.mkString("n")
List(5, 2, 6, 1, 7, 4, 0, 3)
41
def show(queens: List[Int]): String =
val lines: List[String] =
for (col <- queens.reverse)
yield Vector.fill(queens.length)("❎ ")
.updated(col, s"πŸ‘‘ ")
.mkString
"n" + lines.mkString("n")
println( show( List(5, 2, 6, 1, 7, 4, 0, 3) ) )
42
def show(queens: List[Int]): String =
val lines: List[String] =
for (col <- queens.reverse)
yield Vector.fill(queens.length)("❎ ")
.updated(col, s"πŸ‘‘ ")
.mkString
"n" + lines.mkString("n")
❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎
πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘
❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎
❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎
43
Compositional Vector Graphics
44
val redSquare = Image.square(100).fillColor(Color.red)
45
val redSquare = Image.square(100).fillColor(Color.red)
val blueSquare = Image.square(100).fillColor(Color.blue)
46
val redSquare = Image.square(100).fillColor(Color.red)
val blueSquare = Image.square(100).fillColor(Color.blue)
val redBesideBlue = redSquare.beside(blueSquare)
47
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
Solution
49
create
Solution
red and white
square images
50
Image.square
Image.fillColor
create
Solution
red and white
square images
compose
composite
solution image
51
Image.beside
Image.above
Image.square
Image.fillColor
val square: Image = Image.square(100).strokeColor(Color.black)
val emptySquare: Image = square.fillColor(Color.white)
val fullSquare: Image = square.fillColor(Color.orangeRed)
52
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
53
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
m
a
p
54
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
reduce
m
a
p
reduce
reduce
reduce
55
beside
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
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
57
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
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
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
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
62
63
64
65
66
import cats.Monoid
67
assert(Monoid[Int].combine(2,3) == 5)
assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)
import cats.Monoid
import cats.syntax.monoid.*
68
assert(Monoid[Int].combine(2,3) == 5)
assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)
assert((2 |+| 3) == 5)
assert((2 |+| Monoid[Int].empty) == 2)
import cats.Monoid
import cats.syntax.monoid.*
69
assert(Monoid[Int].combine(2,3) == 5)
assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)
assert((2 |+| 3) == 5)
assert((2 |+| Monoid[Int].empty) == 2)
assert(Monoid[Int].empty == 0)
import cats.Monoid
import cats.syntax.monoid.*
import cats.syntax.foldable.*
70
assert(Monoid[Int].combine(2,3) == 5)
assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)
assert((2 |+| 3) == 5)
assert((2 |+| Monoid[Int].empty) == 2)
assert(Monoid[Int].empty == 0)
assert(List.empty[Int].combineAll == 0)
assert(List(1, 2, 3, 4).combineAll == 10)
trait Foldable[F[_]] {
…
def concatenate[A](as: F[A])(m: Monoid[A]): A =
foldLeft(as)(m.zero)(m.op)
…
}
import cats.Monoid
import cats.syntax.monoid.*
import cats.syntax.foldable.*
71
assert(Monoid[Int].combine(2,3) == 5)
assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)
assert((2 |+| 3) == 5)
assert((2 |+| Monoid[Int].empty) == 2)
assert(Monoid[Int].empty == 0)
assert(List.empty[Int].combineAll == 0)
assert(List(1, 2, 3, 4).combineAll == 10)
val prodMonoid = cats.Monoid.instance[Int](emptyValue = 1, cmb = _ * _)
import cats.Monoid
import cats.syntax.monoid.*
import cats.syntax.foldable.*
72
assert(Monoid[Int].combine(2,3) == 5)
assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)
assert((2 |+| 3) == 5)
assert((2 |+| Monoid[Int].empty) == 2)
assert(Monoid[Int].empty == 0)
assert(List.empty[Int].combineAll == 0)
assert(List(1, 2, 3, 4).combineAll == 10)
val prodMonoid = cats.Monoid.instance[Int](emptyValue = 1, cmb = _ * _)
assert(prodMonoid.combine(2, 3) == 6)
assert(prodMonoid.combine(3, prodMonoid.empty) == 3)
import cats.Monoid
import cats.syntax.monoid.*
import cats.syntax.foldable.*
73
assert(Monoid[Int].combine(2,3) == 5)
assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)
assert((2 |+| 3) == 5)
assert((2 |+| Monoid[Int].empty) == 2)
assert(Monoid[Int].empty == 0)
assert(List.empty[Int].combineAll == 0)
assert(List(1, 2, 3, 4).combineAll == 10)
val prodMonoid = cats.Monoid.instance[Int](emptyValue = 1, cmb = _ * _)
assert(prodMonoid.combine(2, 3) == 6)
assert(prodMonoid.combine(3, prodMonoid.empty) == 3)
assert(prodMonoid.empty == 1)
import cats.Monoid
import cats.syntax.monoid.*
import cats.syntax.foldable.*
74
assert(Monoid[Int].combine(2,3) == 5)
assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)
assert((2 |+| 3) == 5)
assert((2 |+| Monoid[Int].empty) == 2)
assert(Monoid[Int].empty == 0)
assert(List.empty[Int].combineAll == 0)
assert(List(1, 2, 3, 4).combineAll == 10)
val prodMonoid = cats.Monoid.instance[Int](emptyValue = 1, cmb = _ * _)
assert(prodMonoid.combine(2, 3) == 6)
assert(prodMonoid.combine(3, prodMonoid.empty) == 3)
assert(prodMonoid.empty == 1)
assert(List.empty[Int].combineAll(prodMonoid) == 1)
assert(List(1, 2, 3, 4).combineAll(prodMonoid) == 24)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.foldLeft(Image.empty)(_ beside _))
.foldLeft(Image.empty)(_ above _)
75
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.foldLeft(Image.empty)(_ beside _))
.foldLeft(Image.empty)(_ above _)
import cats.Monoid
import cats.implicits.*
val beside = Monoid.instance[Image](Image.empty, _ beside _)
76
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.foldLeft(Image.empty)(_ beside _))
.foldLeft(Image.empty)(_ above _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.combineAll(beside))
.foldLeft(Image.empty)(_ above _)
import cats.Monoid
import cats.implicits.*
val beside = Monoid.instance[Image](Image.empty, _ beside _)
77
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
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
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.combineAll(beside))
.foldLeft(Image.empty)(_ above _)
80
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
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
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.combineAll(beside))
.reduce(_ above _)
MapReduce is the marketing buzzword for foldMap
83
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.combineAll(beside))
.reduce(_ above _)
val above = Monoid.instance[Image](Image.empty, _ above _)
MapReduce is the marketing buzzword for foldMap
84
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.combineAll(beside))
.reduce(_ above _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_.combineAll(beside))(above)
val above = Monoid.instance[Image](Image.empty, _ above _)
MapReduce is the marketing buzzword for foldMap
85
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_.combineAll(beside))(above)
RECAP
86
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_.fold(beside))(above)
fold
f
o
l
d
M
a
p
fold
fold
fold
87
beside
above
def show(queens: List[Int]): String =
val lines: List[String] =
for (col <- queens.reverse)
yield Vector.fill(queens.length)("❎ ")
.updated(col, s"πŸ‘‘ ")
.mkString
"n" + lines.mkString("n")
88
❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎
πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘
❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎
❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎
def show(queens: List[Int]): String =
val lines: List[String] =
for (col <- queens.reverse)
yield Vector.fill(queens.length)("❎ ")
.updated(col, s"πŸ‘‘ ")
.mkString
"n" + lines.mkString("n")
89
def show(queens: List[Int]): Image =
val squareImageGrid: List[List[Image]] =
for col <- queens.reverse
yield List.fill(queens.length)(emptySquare)
.updated(col,fullSquare)
combine(squareImageGrid)
❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎
πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘
❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎
❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎
def show(queens: List[Int]): String =
val lines: List[String] =
for (col <- queens.reverse)
yield Vector.fill(queens.length)("❎ ")
.updated(col, s"πŸ‘‘ ")
.mkString
"n" + lines.mkString("n")
❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎
πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘
❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎
❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎
❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎
90
def show(queens: List[Int]): Image =
val squareImageGrid: List[List[Image]] =
for col <- queens.reverse
yield List.fill(queens.length)(emptySquare)
.updated(col,fullSquare)
combine(squareImageGrid)
91
We can do a bit more though
92
93
The images that we have been creating up to now have been centered on the origin of the coordinate system
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
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll beside)(above)
95
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll beside)(above)
val on = Monoid.instance[Image](Image.empty, _ on _)
96
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll on)(on)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll beside)(above)
val on = Monoid.instance[Image](Image.empty, _ on _)
97
val on = Monoid.instance[Image](Image.empty, _ on _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll on)(on)
98
given Monoid.instance[Image] = Monoid.instance[Image](Image.empty, _ on _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll)
val on = Monoid.instance[Image](Image.empty, _ on _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll on)(on)
99
def combine(imageGrid: List[List[Image]]): Image =
imageGrid
.map(_.reduce(_ beside _))
.reduce(_ above _)
FULL RECAP
100
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
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
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
How do we compute the permutations of N queens, e.g. for N=4?
104
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
List(
List(1, 1, 1, 1), List(1, 1, 1, 2), List(1, 1, 1, 3), List(1, 1, 1, 4), List(1, 1, 2, 1), List(1, 1, 2, 2),
List(1, 1, 2, 3), List(1, 1, 2, 4), List(1, 1, 3, 1), List(1, 1, 3, 2), List(1, 1, 3, 3), List(1, 1, 3, 4),
List(1, 1, 4, 1), List(1, 1, 4, 2), List(1, 1, 4, 3), List(1, 1, 4, 4), List(1, 2, 1, 1), List(1, 2, 1, 2),
List(1, 2, 1, 3), List(1, 2, 1, 4), List(1, 2, 2, 1), List(1, 2, 2, 2), List(1, 2, 2, 3), List(1, 2, 2, 4),
List(1, 2, 3, 1), List(1, 2, 3, 2), List(1, 2, 3, 3), List(1, 2, 3, 4), List(1, 2, 4, 1), List(1, 2, 4, 2),
List(1, 2, 4, 3), List(1, 2, 4, 4), List(1, 3, 1, 1), List(1, 3, 1, 2), List(1, 3, 1, 3), List(1, 3, 1, 4),
List(1, 3, 2, 1), List(1, 3, 2, 2), List(1, 3, 2, 3), List(1, 3, 2, 4), List(1, 3, 3, 1), List(1, 3, 3, 2),
List(1, 3, 3, 3), List(1, 3, 3, 4), List(1, 3, 4, 1), List(1, 3, 4, 2), List(1, 3, 4, 3), List(1, 3, 4, 4),
List(1, 4, 1, 1), List(1, 4, 1, 2), List(1, 4, 1, 3), List(1, 4, 1, 4), List(1, 4, 2, 1), List(1, 4, 2, 2),
List(1, 4, 2, 3), List(1, 4, 2, 4), List(1, 4, 3, 1), List(1, 4, 3, 2), List(1, 4, 3, 3), List(1, 4, 3, 4),
List(1, 4, 4, 1), List(1, 4, 4, 2), List(1, 4, 4, 3), List(1, 4, 4, 4), List(2, 1, 1, 1), List(2, 1, 1, 2),
List(2, 1, 1, 3), List(2, 1, 1, 4), List(2, 1, 2, 1), List(2, 1, 2, 2), List(2, 1, 2, 3), List(2, 1, 2, 4),
List(2, 1, 3, 1), List(2, 1, 3, 2), List(2, 1, 3, 3), List(2, 1, 3, 4), List(2, 1, 4, 1), List(2, 1, 4, 2),
List(2, 1, 4, 3), List(2, 1, 4, 4), List(2, 2, 1, 1), List(2, 2, 1, 2), List(2, 2, 1, 3), List(2, 2, 1, 4),
List(2, 2, 2, 1), List(2, 2, 2, 2), List(2, 2, 2, 3), List(2, 2, 2, 4), List(2, 2, 3, 1), List(2, 2, 3, 2),
List(2, 2, 3, 3), List(2, 2, 3, 4), List(2, 2, 4, 1), List(2, 2, 4, 2), List(2, 2, 4, 3), List(2, 2, 4, 4),
List(2, 3, 1, 1), List(2, 3, 1, 2), List(2, 3, 1, 3), List(2, 3, 1, 4), List(2, 3, 2, 1), List(2, 3, 2, 2),
List(2, 3, 2, 3), List(2, 3, 2, 4), List(2, 3, 3, 1), List(2, 3, 3, 2), List(2, 3, 3, 3), List(2, 3, 3, 4),
List(2, 3, 4, 1), List(2, 3, 4, 2), List(2, 3, 4, 3), List(2, 3, 4, 4), List(2, 4, 1, 1), List(2, 4, 1, 2),
List(2, 4, 1, 3), List(2, 4, 1, 4), List(2, 4, 2, 1), List(2, 4, 2, 2), List(2, 4, 2, 3), List(2, 4, 2, 4),
List(2, 4, 3, 1), List(2, 4, 3, 2), List(2, 4, 3, 3), List(2, 4, 3, 4), List(2, 4, 4, 1), List(2, 4, 4, 2),
List(2, 4, 4, 3), List(2, 4, 4, 4), List(3, 1, 1, 1), List(3, 1, 1, 2), List(3, 1, 1, 3), List(3, 1, 1, 4),
List(3, 1, 2, 1), List(3, 1, 2, 2), List(3, 1, 2, 3), List(3, 1, 2, 4), List(3, 1, 3, 1), List(3, 1, 3, 2),
List(3, 1, 3, 3), List(3, 1, 3, 4), List(3, 1, 4, 1), List(3, 1, 4, 2), List(3, 1, 4, 3), List(3, 1, 4, 4),
List(3, 2, 1, 1), List(3, 2, 1, 2), List(3, 2, 1, 3), List(3, 2, 1, 4), List(3, 2, 2, 1), List(3, 2, 2, 2),
List(3, 2, 2, 3), List(3, 2, 2, 4), List(3, 2, 3, 1), List(3, 2, 3, 2), List(3, 2, 3, 3), List(3, 2, 3, 4),
List(3, 2, 4, 1), List(3, 2, 4, 2), List(3, 2, 4, 3), List(3, 2, 4, 4), List(3, 3, 1, 1), List(3, 3, 1, 2),
List(3, 3, 1, 3), List(3, 3, 1, 4), List(3, 3, 2, 1), List(3, 3, 2, 2), List(3, 3, 2, 3), List(3, 3, 2, 4),
List(3, 3, 3, 1), List(3, 3, 3, 2), List(3, 3, 3, 3), List(3, 3, 3, 4), List(3, 3, 4, 1), List(3, 3, 4, 2),
List(3, 3, 4, 3), List(3, 3, 4, 4), List(3, 4, 1, 1), List(3, 4, 1, 2), List(3, 4, 1, 3), List(3, 4, 1, 4),
List(3, 4, 2, 1), List(3, 4, 2, 2), List(3, 4, 2, 3), List(3, 4, 2, 4), List(3, 4, 3, 1), List(3, 4, 3, 2),
List(3, 4, 3, 3), List(3, 4, 3, 4), List(3, 4, 4, 1), List(3, 4, 4, 2), List(3, 4, 4, 3), List(3, 4, 4, 4),
List(4, 1, 1, 1), List(4, 1, 1, 2), List(4, 1, 1, 3), List(4, 1, 1, 4), List(4, 1, 2, 1), List(4, 1, 2, 2),
List(4, 1, 2, 3), List(4, 1, 2, 4), List(4, 1, 3, 1), List(4, 1, 3, 2), List(4, 1, 3, 3), List(4, 1, 3, 4),
List(4, 1, 4, 1), List(4, 1, 4, 2), List(4, 1, 4, 3), List(4, 1, 4, 4), List(4, 2, 1, 1), List(4, 2, 1, 2),
List(4, 2, 1, 3), List(4, 2, 1, 4), List(4, 2, 2, 1), List(4, 2, 2, 2), List(4, 2, 2, 3), List(4, 2, 2, 4),
List(4, 2, 3, 1), List(4, 2, 3, 2), List(4, 2, 3, 3), List(4, 2, 3, 4), List(4, 2, 4, 1), List(4, 2, 4, 2),
List(4, 2, 4, 3), List(4, 2, 4, 4), List(4, 3, 1, 1), List(4, 3, 1, 2), List(4, 3, 1, 3), List(4, 3, 1, 4),
List(4, 3, 2, 1), List(4, 3, 2, 2), List(4, 3, 2, 3), List(4, 3, 2, 4), List(4, 3, 3, 1), List(4, 3, 3, 2),
List(4, 3, 3, 3), List(4, 3, 3, 4), List(4, 3, 4, 1), List(4, 3, 4, 2), List(4, 3, 4, 3), List(4, 3, 4, 4),
List(4, 4, 1, 1), List(4, 4, 1, 2), List(4, 4, 1, 3), List(4, 4, 1, 4), List(4, 4, 2, 1), List(4, 4, 2, 2),
List(4, 4, 2, 3), List(4, 4, 2, 4), List(4, 4, 3, 1), List(4, 4, 3, 2), List(4, 4, 3, 3), List(4, 4, 3, 4),
List(4, 4, 4, 1), List(4, 4, 4, 2), List(4, 4, 4, 3), List(4, 4, 4, 4))
106
107
# of results = 256 (size of result)
# of permutations with repetition allowed = 256 (44)
# of permutations with repetition disallowed = 24 (4!)
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
println(s"# of results = ${results.size}")
println(s"# of permutations with repetition allowed = ${4 * 4 * 4 * 4}") // NN
println(s"# of permutations with repetition disallowed = ${4 * 3 * 2 * 1}") // N!
N = 4
# of permutations = 44 = 256
108
N = 4
# of permutations = 44 = 256
How many of these
are solutions?
109
N N-queens solution count
1 1
2 0
3 0
4 2
5 10
6 4
7 40
8 92
9 352
10 724
11 2,680
12 14,200
13 73,712
14 365,596
15 2,279,184
16 14,772,512
17 95,815,104
18 666,090,624
19 4,968,057,848
20 39,029,188,884
21 314,666,222,712
22 2,691,008,701,644
23 24,233,937,684,440
24 227,514,171,973,736
25 2,207,893,435,808,352
26 22,317,699,616,364,044
27 234,907,967,154,122,528
data source: https://en.wikipedia.org/wiki/Eight_queens_puzzle
110
N = 4
# of permutations = 44 = 256
# of solutions = 2
111
How do we find the solutions?
112
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
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
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
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
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
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
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
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
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
N = 4
# of permutations = 44 = 256
Same result as before
using recursion
122
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
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
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
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
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
List(
List(1, 1, 1, 1), List(1, 1, 1, 2), List(1, 1, 1, 3), List(1, 1, 1, 4), List(1, 1, 2, 1), List(1, 1, 2, 2),
List(1, 1, 2, 3), List(1, 1, 2, 4), List(1, 1, 3, 1), List(1, 1, 3, 2), List(1, 1, 3, 3), List(1, 1, 3, 4),
List(1, 1, 4, 1), List(1, 1, 4, 2), List(1, 1, 4, 3), List(1, 1, 4, 4), List(1, 2, 1, 1), List(1, 2, 1, 2),
List(1, 2, 1, 3), List(1, 2, 1, 4), List(1, 2, 2, 1), List(1, 2, 2, 2), List(1, 2, 2, 3), List(1, 2, 2, 4),
List(1, 2, 3, 1), List(1, 2, 3, 2), List(1, 2, 3, 3), List(1, 2, 3, 4), List(1, 2, 4, 1), List(1, 2, 4, 2),
List(1, 2, 4, 3), List(1, 2, 4, 4), List(1, 3, 1, 1), List(1, 3, 1, 2), List(1, 3, 1, 3), List(1, 3, 1, 4),
List(1, 3, 2, 1), List(1, 3, 2, 2), List(1, 3, 2, 3), List(1, 3, 2, 4), List(1, 3, 3, 1), List(1, 3, 3, 2),
List(1, 3, 3, 3), List(1, 3, 3, 4), List(1, 3, 4, 1), List(1, 3, 4, 2), List(1, 3, 4, 3), List(1, 3, 4, 4),
List(1, 4, 1, 1), List(1, 4, 1, 2), List(1, 4, 1, 3), List(1, 4, 1, 4), List(1, 4, 2, 1), List(1, 4, 2, 2),
List(1, 4, 2, 3), List(1, 4, 2, 4), List(1, 4, 3, 1), List(1, 4, 3, 2), List(1, 4, 3, 3), List(1, 4, 3, 4),
List(1, 4, 4, 1), List(1, 4, 4, 2), List(1, 4, 4, 3), List(1, 4, 4, 4), List(2, 1, 1, 1), List(2, 1, 1, 2),
List(2, 1, 1, 3), List(2, 1, 1, 4), List(2, 1, 2, 1), List(2, 1, 2, 2), List(2, 1, 2, 3), List(2, 1, 2, 4),
List(2, 1, 3, 1), List(2, 1, 3, 2), List(2, 1, 3, 3), List(2, 1, 3, 4), List(2, 1, 4, 1), List(2, 1, 4, 2),
List(2, 1, 4, 3), List(2, 1, 4, 4), List(2, 2, 1, 1), List(2, 2, 1, 2), List(2, 2, 1, 3), List(2, 2, 1, 4),
List(2, 2, 2, 1), List(2, 2, 2, 2), List(2, 2, 2, 3), List(2, 2, 2, 4), List(2, 2, 3, 1), List(2, 2, 3, 2),
List(2, 2, 3, 3), List(2, 2, 3, 4), List(2, 2, 4, 1), List(2, 2, 4, 2), List(2, 2, 4, 3), List(2, 2, 4, 4),
List(2, 3, 1, 1), List(2, 3, 1, 2), List(2, 3, 1, 3), List(2, 3, 1, 4), List(2, 3, 2, 1), List(2, 3, 2, 2),
List(2, 3, 2, 3), List(2, 3, 2, 4), List(2, 3, 3, 1), List(2, 3, 3, 2), List(2, 3, 3, 3), List(2, 3, 3, 4),
List(2, 3, 4, 1), List(2, 3, 4, 2), List(2, 3, 4, 3), List(2, 3, 4, 4), List(2, 4, 1, 1), List(2, 4, 1, 2),
List(2, 4, 1, 3), List(2, 4, 1, 4), List(2, 4, 2, 1), List(2, 4, 2, 2), List(2, 4, 2, 3), List(2, 4, 2, 4),
List(2, 4, 3, 1), List(2, 4, 3, 2), List(2, 4, 3, 3), List(2, 4, 3, 4), List(2, 4, 4, 1), List(2, 4, 4, 2),
List(2, 4, 4, 3), List(2, 4, 4, 4), List(3, 1, 1, 1), List(3, 1, 1, 2), List(3, 1, 1, 3), List(3, 1, 1, 4),
List(3, 1, 2, 1), List(3, 1, 2, 2), List(3, 1, 2, 3), List(3, 1, 2, 4), List(3, 1, 3, 1), List(3, 1, 3, 2),
List(3, 1, 3, 3), List(3, 1, 3, 4), List(3, 1, 4, 1), List(3, 1, 4, 2), List(3, 1, 4, 3), List(3, 1, 4, 4),
List(3, 2, 1, 1), List(3, 2, 1, 2), List(3, 2, 1, 3), List(3, 2, 1, 4), List(3, 2, 2, 1), List(3, 2, 2, 2),
List(3, 2, 2, 3), List(3, 2, 2, 4), List(3, 2, 3, 1), List(3, 2, 3, 2), List(3, 2, 3, 3), List(3, 2, 3, 4),
List(3, 2, 4, 1), List(3, 2, 4, 2), List(3, 2, 4, 3), List(3, 2, 4, 4), List(3, 3, 1, 1), List(3, 3, 1, 2),
List(3, 3, 1, 3), List(3, 3, 1, 4), List(3, 3, 2, 1), List(3, 3, 2, 2), List(3, 3, 2, 3), List(3, 3, 2, 4),
List(3, 3, 3, 1), List(3, 3, 3, 2), List(3, 3, 3, 3), List(3, 3, 3, 4), List(3, 3, 4, 1), List(3, 3, 4, 2),
List(3, 3, 4, 3), List(3, 3, 4, 4), List(3, 4, 1, 1), List(3, 4, 1, 2), List(3, 4, 1, 3), List(3, 4, 1, 4),
List(3, 4, 2, 1), List(3, 4, 2, 2), List(3, 4, 2, 3), List(3, 4, 2, 4), List(3, 4, 3, 1), List(3, 4, 3, 2),
List(3, 4, 3, 3), List(3, 4, 3, 4), List(3, 4, 4, 1), List(3, 4, 4, 2), List(3, 4, 4, 3), List(3, 4, 4, 4),
List(4, 1, 1, 1), List(4, 1, 1, 2), List(4, 1, 1, 3), List(4, 1, 1, 4), List(4, 1, 2, 1), List(4, 1, 2, 2),
List(4, 1, 2, 3), List(4, 1, 2, 4), List(4, 1, 3, 1), List(4, 1, 3, 2), List(4, 1, 3, 3), List(4, 1, 3, 4),
List(4, 1, 4, 1), List(4, 1, 4, 2), List(4, 1, 4, 3), List(4, 1, 4, 4), List(4, 2, 1, 1), List(4, 2, 1, 2),
List(4, 2, 1, 3), List(4, 2, 1, 4), List(4, 2, 2, 1), List(4, 2, 2, 2), List(4, 2, 2, 3), List(4, 2, 2, 4),
List(4, 2, 3, 1), List(4, 2, 3, 2), List(4, 2, 3, 3), List(4, 2, 3, 4), List(4, 2, 4, 1), List(4, 2, 4, 2),
List(4, 2, 4, 3), List(4, 2, 4, 4), List(4, 3, 1, 1), List(4, 3, 1, 2), List(4, 3, 1, 3), List(4, 3, 1, 4),
List(4, 3, 2, 1), List(4, 3, 2, 2), List(4, 3, 2, 3), List(4, 3, 2, 4), List(4, 3, 3, 1), List(4, 3, 3, 2),
List(4, 3, 3, 3), List(4, 3, 3, 4), List(4, 3, 4, 1), List(4, 3, 4, 2), List(4, 3, 4, 3), List(4, 3, 4, 4),
List(4, 4, 1, 1), List(4, 4, 1, 2), List(4, 4, 1, 3), List(4, 4, 1, 4), List(4, 4, 2, 1), List(4, 4, 2, 2),
List(4, 4, 2, 3), List(4, 4, 2, 4), List(4, 4, 3, 1), List(4, 4, 3, 2), List(4, 4, 3, 3), List(4, 4, 3, 4),
List(4, 4, 4, 1), List(4, 4, 4, 2), List(4, 4, 4, 3), List(4, 4, 4, 4))
List(
List(3,1,4,2),
List(2,4,1,3))
)
BEFORE AFTER
adding filtering
(isSafe function)
128
BEFORE AFTER
adding filtering (isSafe function)
129
Let’s visualise how the filtering reduces the problem space
130
131
132
133
134
Candidate boards generated and tested: 60 (out of 256) 135
N = 4
2 solutions
136
N = 5
10 solutions
137
N = 6
4 solutions
138
N = 7
40 solutions
139
N = 8
92 solutions
140
92 boards
N = 5
N = 6
N = 4
N = 7
N = 8
40 boards
4 boards
10 boards
2 boards
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
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
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
We are all very familiar with the 3 functions that are the bread, butter, and jam of Functional Programming
145
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
map
Ξ»
mapM
Ξ»
Let’s gain an understanding of the foldM function by looking at the monadic variant of the triad
147
Ξ»
mapM
148
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
map :: (a -> b) -> [a] -> [b]
150
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
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
mapM
?
?
153
Paul Chiusano
Runar Bjarnason
@pchiusano
@runarorama
FP in Scala
There turns out to be a startling number of operations that can be defined
in the most general possible way in terms of sequence and/or traverse
154
A quick refresher on the traverse function
155
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.
scala> import scala.concurrent.Future
| import concurrent.ExecutionContext.Implicits.global
157
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
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
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
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
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
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
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
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
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
That was the quick refresher on the traverse function
167
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
168
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
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
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
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
/**
* 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
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
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…
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…
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
A quick mention of the Symmetry that exists in the interrelation of
flatMap/foldMap/traverse and flatten/fold/sequence
178
def flatMap[A,B](ma: F[A])(f: A β‡’ F[B]): F[B] = flatten(map(ma)(f))
179
def flatMap[A,B](ma: F[A])(f: A β‡’ F[B]): F[B] = flatten(map(ma)(f))
def flatten[A](mma: F[F[A]]): F[A] = flatMap(mma)(x β‡’ x)
180
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 flatten[A](mma: F[F[A]]): F[A] = flatMap(mma)(x β‡’ x)
181
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 flatten[A](mma: F[F[A]]): F[A] = flatMap(mma)(x β‡’ x)
def fold[A:Monoid](fa: F[A]): A = foldMap(fa)(x β‡’ x)
182
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)
183
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)
184
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
Examples of mapM usage
Example Monadic Context How the result of mapM is affected
1
2
3
186
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
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
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
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
β€’ 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
β€’ 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
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
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
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
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
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
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
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
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
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")
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")
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")
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")
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()
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()
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()
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")
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")
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")
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")
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")
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")
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()
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()
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
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
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
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
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
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
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
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
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
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
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
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
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
import cats.effect.IO
import cats.effect.unsafe.implicits.global
import scala.io.StdIn.readLine
def getNumber(msg: String): IO[Int] =
(IO.print(msg) *> IO.readLine).map(_.toIntOption.getOrElse(0))
229
side effects
suspended in
pure value
import cats.effect.IO
import cats.effect.unsafe.implicits.global
import scala.io.StdIn.readLine
def getNumber(msg: String): IO[Int] =
(IO.print(msg) *> IO.readLine).map(_.toIntOption.getOrElse(0))
val messages = List("Enter a number: ", "Enter another: ")
230
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
1 of 386

Recommended

Monads do not Compose by
Monads do not ComposeMonads do not Compose
Monads do not ComposePhilip Schwarz
2.1K viewsβ€’30 slides
Domain Modeling Made Functional (KanDDDinsky 2019) by
Domain Modeling Made Functional (KanDDDinsky 2019)Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)Scott Wlaschin
3K viewsβ€’127 slides
Http4s, Doobie and Circe: The Functional Web Stack by
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackGaryCoady
6.3K viewsβ€’33 slides
Domain Modeling with FP (DDD Europe 2020) by
Domain Modeling with FP (DDD Europe 2020)Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)Scott Wlaschin
1.5K viewsβ€’95 slides
Why The Free Monad isn't Free by
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't FreeKelley Robinson
16K viewsβ€’103 slides
C++ Inheritance by
C++ InheritanceC++ Inheritance
C++ InheritanceJussi Pohjolainen
13.3K viewsβ€’29 slides

More Related Content

What's hot

Kotlin by
KotlinKotlin
KotlinAhmad Mahagna
221 viewsβ€’34 slides
Interfaces c# by
Interfaces c#Interfaces c#
Interfaces c#Nipam Medhi
3.1K viewsβ€’17 slides
Functional Design Patterns (DevTernity 2018) by
Functional Design Patterns (DevTernity 2018)Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)Scott Wlaschin
5.5K viewsβ€’202 slides
Lazy java by
Lazy javaLazy java
Lazy javaMario Fusco
6.3K viewsβ€’66 slides
Refactoring Functional Type Classes by
Refactoring Functional Type ClassesRefactoring Functional Type Classes
Refactoring Functional Type ClassesJohn De Goes
6.9K viewsβ€’74 slides
End to end todo list app with NestJs - Angular - Redux & Redux Saga by
End to end todo list app with NestJs - Angular - Redux & Redux SagaEnd to end todo list app with NestJs - Angular - Redux & Redux Saga
End to end todo list app with NestJs - Angular - Redux & Redux SagaBabacar NIANG
489 viewsβ€’17 slides

What's hot(20)

Interfaces c# by Nipam Medhi
Interfaces c#Interfaces c#
Interfaces c#
Nipam Medhiβ€’3.1K views
Functional Design Patterns (DevTernity 2018) by Scott Wlaschin
Functional Design Patterns (DevTernity 2018)Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)
Scott Wlaschinβ€’5.5K views
Lazy java by Mario Fusco
Lazy javaLazy java
Lazy java
Mario Fuscoβ€’6.3K views
Refactoring Functional Type Classes by John De Goes
Refactoring Functional Type ClassesRefactoring Functional Type Classes
Refactoring Functional Type Classes
John De Goesβ€’6.9K views
End to end todo list app with NestJs - Angular - Redux & Redux Saga by Babacar NIANG
End to end todo list app with NestJs - Angular - Redux & Redux SagaEnd to end todo list app with NestJs - Angular - Redux & Redux Saga
End to end todo list app with NestJs - Angular - Redux & Redux Saga
Babacar NIANGβ€’489 views
The Power of Composition by Scott Wlaschin
The Power of CompositionThe Power of Composition
The Power of Composition
Scott Wlaschinβ€’7.2K views
Android styles and themes by Deepa Rani
Android   styles and themesAndroid   styles and themes
Android styles and themes
Deepa Raniβ€’1.2K views
Domain Driven Design with the F# type System -- NDC London 2013 by Scott Wlaschin
Domain Driven Design with the F# type System -- NDC London 2013Domain Driven Design with the F# type System -- NDC London 2013
Domain Driven Design with the F# type System -- NDC London 2013
Scott Wlaschinβ€’26.6K views
Serverless Clouds (FaaS) and request context isolation in Node.js by Timur Shemsedinov
Serverless Clouds (FaaS) and request context isolation in Node.jsServerless Clouds (FaaS) and request context isolation in Node.js
Serverless Clouds (FaaS) and request context isolation in Node.js
Timur Shemsedinovβ€’323 views
Laravel Design Patterns by Bobby Bouwmann
Laravel Design PatternsLaravel Design Patterns
Laravel Design Patterns
Bobby Bouwmannβ€’24.2K views
Overriding by adil raja
OverridingOverriding
Overriding
adil rajaβ€’1.7K views
The Functional Programmer's Toolkit (NDC London 2019) by Scott Wlaschin
The Functional Programmer's Toolkit (NDC London 2019)The Functional Programmer's Toolkit (NDC London 2019)
The Functional Programmer's Toolkit (NDC London 2019)
Scott Wlaschinβ€’1.9K views
How to build a chat application with react js, nodejs, and socket.io by Katy Slemon
How to build a chat application with react js, nodejs, and socket.ioHow to build a chat application with react js, nodejs, and socket.io
How to build a chat application with react js, nodejs, and socket.io
Katy Slemonβ€’629 views
The Terror-Free Guide to Introducing Functional Scala at Work by Jorge VΓ‘squez
The Terror-Free Guide to Introducing Functional Scala at WorkThe Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at Work
Jorge VΓ‘squezβ€’766 views
Beyond Scala Lens by Julien Truffaut
Beyond Scala LensBeyond Scala Lens
Beyond Scala Lens
Julien Truffautβ€’12.7K views
Implicit parameters, when to use them (or not)! by Julien Truffaut
Implicit parameters, when to use them (or not)!Implicit parameters, when to use them (or not)!
Implicit parameters, when to use them (or not)!
Julien Truffautβ€’294 views

More from Philip Schwarz

Scala Left Fold Parallelisation - Three Approaches by
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three ApproachesPhilip Schwarz
43 viewsβ€’44 slides
Tagless Final Encoding - Algebras and Interpreters and also Programs by
Tagless Final Encoding - Algebras and Interpreters and also ProgramsTagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also ProgramsPhilip Schwarz
46 viewsβ€’16 slides
Fusing Transformations of Strict Scala Collections with Views by
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsPhilip Schwarz
20 viewsβ€’28 slides
A sighting of sequence function in Practical FP in Scala by
A sighting of sequence function in Practical FP in ScalaA sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in ScalaPhilip Schwarz
28 viewsβ€’4 slides
Kleisli composition, flatMap, join, map, unit - implementation and interrelat... by
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Philip Schwarz
16 viewsβ€’16 slides
The aggregate function - from sequential and parallel folds to parallel aggre... by
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...Philip Schwarz
264 viewsβ€’31 slides

More from Philip Schwarz(20)

Scala Left Fold Parallelisation - Three Approaches by Philip Schwarz
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three Approaches
Philip Schwarzβ€’43 views
Tagless Final Encoding - Algebras and Interpreters and also Programs by Philip Schwarz
Tagless Final Encoding - Algebras and Interpreters and also ProgramsTagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also Programs
Philip Schwarzβ€’46 views
Fusing Transformations of Strict Scala Collections with Views by Philip Schwarz
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with Views
Philip Schwarzβ€’20 views
A sighting of sequence function in Practical FP in Scala by Philip Schwarz
A sighting of sequence function in Practical FP in ScalaA sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in Scala
Philip Schwarzβ€’28 views
Kleisli composition, flatMap, join, map, unit - implementation and interrelat... by Philip Schwarz
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Philip Schwarzβ€’16 views
The aggregate function - from sequential and parallel folds to parallel aggre... by Philip Schwarz
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
Philip Schwarzβ€’264 views
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske... by Philip Schwarz
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
Philip Schwarzβ€’250 views
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha... by Philip Schwarz
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Philip Schwarzβ€’827 views
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t... by Philip Schwarz
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Philip Schwarzβ€’1.2K views
Jordan Peterson - The pursuit of meaning and related ethical axioms by Philip Schwarz
Jordan Peterson - The pursuit of meaning and related ethical axiomsJordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axioms
Philip Schwarzβ€’141 views
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c... by Philip Schwarz
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Philip Schwarzβ€’100 views
Defining filter using (a) recursion (b) folding with S, B and I combinators (... by Philip Schwarz
Defining filter using (a) recursion (b) folding with S, B and I combinators (...Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Philip Schwarzβ€’78 views
The Sieve of Eratosthenes - Part 1 - with minor corrections by Philip Schwarz
The Sieve of Eratosthenes - Part 1 - with minor correctionsThe Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor corrections
Philip Schwarzβ€’110 views
The Sieve of Eratosthenes - Part 1 by Philip Schwarz
The Sieve of Eratosthenes - Part 1The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1
Philip Schwarzβ€’1K views
The Uniform Access Principle by Philip Schwarz
The Uniform Access PrincipleThe Uniform Access Principle
The Uniform Access Principle
Philip Schwarzβ€’542 views
Computer Graphics in Java and Scala - Part 1b by Philip Schwarz
Computer Graphics in Java and Scala - Part 1bComputer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1b
Philip Schwarzβ€’261 views
The Expression Problem - Part 2 by Philip Schwarz
The Expression Problem - Part 2The Expression Problem - Part 2
The Expression Problem - Part 2
Philip Schwarzβ€’650 views
Computer Graphics in Java and Scala - Part 1 by Philip Schwarz
Computer Graphics in Java and Scala - Part 1Computer Graphics in Java and Scala - Part 1
Computer Graphics in Java and Scala - Part 1
Philip Schwarzβ€’403 views
The Expression Problem - Part 1 by Philip Schwarz
The Expression Problem - Part 1The Expression Problem - Part 1
The Expression Problem - Part 1
Philip Schwarzβ€’1.2K views
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac... by Philip Schwarz
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Philip Schwarzβ€’268 views

Recently uploaded

predicting-m3-devopsconMunich-2023.pptx by
predicting-m3-devopsconMunich-2023.pptxpredicting-m3-devopsconMunich-2023.pptx
predicting-m3-devopsconMunich-2023.pptxTier1 app
8 viewsβ€’24 slides
Ports-and-Adapters Architecture for Embedded HMI by
Ports-and-Adapters Architecture for Embedded HMIPorts-and-Adapters Architecture for Embedded HMI
Ports-and-Adapters Architecture for Embedded HMIBurkhard Stubert
33 viewsβ€’19 slides
360 graden fabriek by
360 graden fabriek360 graden fabriek
360 graden fabriekinfo33492
165 viewsβ€’25 slides
Automated Testing of Microsoft Power BI Reports by
Automated Testing of Microsoft Power BI ReportsAutomated Testing of Microsoft Power BI Reports
Automated Testing of Microsoft Power BI ReportsRTTS
10 viewsβ€’20 slides
Playwright Retries by
Playwright RetriesPlaywright Retries
Playwright Retriesartembondar5
6 viewsβ€’1 slide
Benefits in Software Development by
Benefits in Software DevelopmentBenefits in Software Development
Benefits in Software DevelopmentJohn Valentino
5 viewsβ€’15 slides

Recently uploaded(20)

predicting-m3-devopsconMunich-2023.pptx by Tier1 app
predicting-m3-devopsconMunich-2023.pptxpredicting-m3-devopsconMunich-2023.pptx
predicting-m3-devopsconMunich-2023.pptx
Tier1 appβ€’8 views
Ports-and-Adapters Architecture for Embedded HMI by Burkhard Stubert
Ports-and-Adapters Architecture for Embedded HMIPorts-and-Adapters Architecture for Embedded HMI
Ports-and-Adapters Architecture for Embedded HMI
Burkhard Stubertβ€’33 views
360 graden fabriek by info33492
360 graden fabriek360 graden fabriek
360 graden fabriek
info33492β€’165 views
Automated Testing of Microsoft Power BI Reports by RTTS
Automated Testing of Microsoft Power BI ReportsAutomated Testing of Microsoft Power BI Reports
Automated Testing of Microsoft Power BI Reports
RTTSβ€’10 views
Playwright Retries by artembondar5
Playwright RetriesPlaywright Retries
Playwright Retries
artembondar5β€’6 views
Benefits in Software Development by John Valentino
Benefits in Software DevelopmentBenefits in Software Development
Benefits in Software Development
John Valentinoβ€’5 views
Introduction to Gradle by John Valentino
Introduction to GradleIntroduction to Gradle
Introduction to Gradle
John Valentinoβ€’6 views
ADDO_2022_CICID_Tom_Halpin.pdf by TomHalpin9
ADDO_2022_CICID_Tom_Halpin.pdfADDO_2022_CICID_Tom_Halpin.pdf
ADDO_2022_CICID_Tom_Halpin.pdf
TomHalpin9β€’5 views
Electronic AWB - Electronic Air Waybill by Freightoscope
Electronic AWB - Electronic Air Waybill Electronic AWB - Electronic Air Waybill
Electronic AWB - Electronic Air Waybill
Freightoscope β€’5 views
Navigating container technology for enhanced security by Niklas Saari by Metosin Oy
Navigating container technology for enhanced security by Niklas SaariNavigating container technology for enhanced security by Niklas Saari
Navigating container technology for enhanced security by Niklas Saari
Metosin Oyβ€’15 views
Top-5-production-devconMunich-2023-v2.pptx by Tier1 app
Top-5-production-devconMunich-2023-v2.pptxTop-5-production-devconMunich-2023-v2.pptx
Top-5-production-devconMunich-2023-v2.pptx
Tier1 appβ€’8 views
predicting-m3-devopsconMunich-2023-v2.pptx by Tier1 app
predicting-m3-devopsconMunich-2023-v2.pptxpredicting-m3-devopsconMunich-2023-v2.pptx
predicting-m3-devopsconMunich-2023-v2.pptx
Tier1 appβ€’12 views
aATP - New Correlation Confirmation Feature.pptx by EsatEsenek1
aATP - New Correlation Confirmation Feature.pptxaATP - New Correlation Confirmation Feature.pptx
aATP - New Correlation Confirmation Feature.pptx
EsatEsenek1β€’205 views
Generic or specific? Making sensible software design decisions by Bert Jan Schrijver
Generic or specific? Making sensible software design decisionsGeneric or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisions
Bert Jan Schrijverβ€’7 views
How To Make Your Plans Suck Less β€”Β Maarten Dalmijn at the 57th Hands-on Agile... by Stefan Wolpers
How To Make Your Plans Suck Less β€”Β Maarten Dalmijn at the 57th Hands-on Agile...How To Make Your Plans Suck Less β€”Β Maarten Dalmijn at the 57th Hands-on Agile...
How To Make Your Plans Suck Less β€”Β Maarten Dalmijn at the 57th Hands-on Agile...
Stefan Wolpersβ€’42 views
How Workforce Management Software Empowers SMEs | TraQSuite by TraQSuite
How Workforce Management Software Empowers SMEs | TraQSuiteHow Workforce Management Software Empowers SMEs | TraQSuite
How Workforce Management Software Empowers SMEs | TraQSuite
TraQSuiteβ€’6 views
AI and Ml presentation .pptx by FayazAli87
AI and Ml presentation .pptxAI and Ml presentation .pptx
AI and Ml presentation .pptx
FayazAli87β€’14 views
Team Transformation Tactics for Holistic Testing and Quality (Japan Symposium... by Lisi Hocke
Team Transformation Tactics for Holistic Testing and Quality (Japan Symposium...Team Transformation Tactics for Holistic Testing and Quality (Japan Symposium...
Team Transformation Tactics for Holistic Testing and Quality (Japan Symposium...
Lisi Hockeβ€’35 views
Dapr Unleashed: Accelerating Microservice Development by Miroslav Janeski
Dapr Unleashed: Accelerating Microservice DevelopmentDapr Unleashed: Accelerating Microservice Development
Dapr Unleashed: Accelerating Microservice Development
Miroslav Janeskiβ€’15 views

N-Queens Combinatorial Puzzle meets Cats

  • 1. π‘šπ‘Žπ‘π‘€ monadic mapping, filtering, folding π‘“π‘–π‘™π‘‘π‘’π‘Ÿπ‘€ π‘“π‘œπ‘™π‘‘π‘€ N-Queens Combinatorial Puzzle meetsCats monoidal functions π‘“π‘œπ‘™π‘‘ and π‘“π‘œπ‘™π‘‘π‘€π‘Žπ‘
  • 2. 2 https://speakerdeck.com/philipschwarz https://fosstodon.org/@philip_schwarz https://www.slideshare.net/pjschwarz https://twitter.com/philip_schwarz If you decide you like this, you can find more content like it on http://fpilluminated.com https://github.com/philipschwarz https://www.linkedin.com/in/philip-schwarz-70576a17/ Some of the other places where you can find me
  • 3. 3
  • 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
  • 16. Let’s try to find a solution to the puzzle 16
  • 17. 17
  • 18. 18
  • 19. 19
  • 20. 20
  • 21. 21
  • 22. 22
  • 23. 23 We are stuck. There is nowhere to place queen number 6, because every empty cell on the board is in check from queens 1 to 5.
  • 25. 25
  • 26. 26
  • 27. 27
  • 28. 28
  • 29. 29
  • 30. 30
  • 31. 31
  • 32. 32
  • 33. 33
  • 34. 34 We found a solution
  • 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
  • 36. 36
  • 37. (7, 3) (8, 6) (6, 7) (5, 2) (4, 8) (3, 5) (2, 1) (1, 4) R C O O W L List((8, 6), (7, 3), (6, 7), (5, 2), (4, 8), (3, 5), (2, 1), (1, 4)) 37
  • 38. (6, 2) (7, 5) (5, 6) (4, 1) (3, 7) (2, 4) (1, 0) (0, 3) R C O O W L List(5, 2, 6, 1, 7, 4, 0, 3) 38
  • 39. 39
  • 40. def show(queens: List[Int]): String = val lines: List[String] = for (col <- queens.reverse) yield Vector.fill(queens.length)("❎ ") .updated(col, s"πŸ‘‘ ") .mkString "n" + lines.mkString("n") 40
  • 41. def show(queens: List[Int]): String = val lines: List[String] = for (col <- queens.reverse) yield Vector.fill(queens.length)("❎ ") .updated(col, s"πŸ‘‘ ") .mkString "n" + lines.mkString("n") List(5, 2, 6, 1, 7, 4, 0, 3) 41
  • 42. def show(queens: List[Int]): String = val lines: List[String] = for (col <- queens.reverse) yield Vector.fill(queens.length)("❎ ") .updated(col, s"πŸ‘‘ ") .mkString "n" + lines.mkString("n") println( show( List(5, 2, 6, 1, 7, 4, 0, 3) ) ) 42
  • 43. def show(queens: List[Int]): String = val lines: List[String] = for (col <- queens.reverse) yield Vector.fill(queens.length)("❎ ") .updated(col, s"πŸ‘‘ ") .mkString "n" + lines.mkString("n") ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ 43
  • 45. val redSquare = Image.square(100).fillColor(Color.red) 45
  • 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
  • 50. create Solution red and white square images 50 Image.square Image.fillColor
  • 51. create Solution red and white square images compose composite solution image 51 Image.beside Image.above Image.square Image.fillColor
  • 52. val square: Image = Image.square(100).strokeColor(Color.black) val emptySquare: Image = square.fillColor(Color.white) val fullSquare: Image = square.fillColor(Color.orangeRed) 52
  • 53. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) 53
  • 54. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) m a p 54
  • 55. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) reduce m a p reduce reduce reduce 55 beside
  • 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
  • 57. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) 57
  • 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
  • 62. 62
  • 63. 63
  • 64. 64
  • 65. 65
  • 66. 66
  • 67. import cats.Monoid 67 assert(Monoid[Int].combine(2,3) == 5) assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)
  • 68. import cats.Monoid import cats.syntax.monoid.* 68 assert(Monoid[Int].combine(2,3) == 5) assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2) assert((2 |+| 3) == 5) assert((2 |+| Monoid[Int].empty) == 2)
  • 69. import cats.Monoid import cats.syntax.monoid.* 69 assert(Monoid[Int].combine(2,3) == 5) assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2) assert((2 |+| 3) == 5) assert((2 |+| Monoid[Int].empty) == 2) assert(Monoid[Int].empty == 0)
  • 70. import cats.Monoid import cats.syntax.monoid.* import cats.syntax.foldable.* 70 assert(Monoid[Int].combine(2,3) == 5) assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2) assert((2 |+| 3) == 5) assert((2 |+| Monoid[Int].empty) == 2) assert(Monoid[Int].empty == 0) assert(List.empty[Int].combineAll == 0) assert(List(1, 2, 3, 4).combineAll == 10) trait Foldable[F[_]] { … def concatenate[A](as: F[A])(m: Monoid[A]): A = foldLeft(as)(m.zero)(m.op) … }
  • 71. import cats.Monoid import cats.syntax.monoid.* import cats.syntax.foldable.* 71 assert(Monoid[Int].combine(2,3) == 5) assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2) assert((2 |+| 3) == 5) assert((2 |+| Monoid[Int].empty) == 2) assert(Monoid[Int].empty == 0) assert(List.empty[Int].combineAll == 0) assert(List(1, 2, 3, 4).combineAll == 10) val prodMonoid = cats.Monoid.instance[Int](emptyValue = 1, cmb = _ * _)
  • 72. import cats.Monoid import cats.syntax.monoid.* import cats.syntax.foldable.* 72 assert(Monoid[Int].combine(2,3) == 5) assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2) assert((2 |+| 3) == 5) assert((2 |+| Monoid[Int].empty) == 2) assert(Monoid[Int].empty == 0) assert(List.empty[Int].combineAll == 0) assert(List(1, 2, 3, 4).combineAll == 10) val prodMonoid = cats.Monoid.instance[Int](emptyValue = 1, cmb = _ * _) assert(prodMonoid.combine(2, 3) == 6) assert(prodMonoid.combine(3, prodMonoid.empty) == 3)
  • 73. import cats.Monoid import cats.syntax.monoid.* import cats.syntax.foldable.* 73 assert(Monoid[Int].combine(2,3) == 5) assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2) assert((2 |+| 3) == 5) assert((2 |+| Monoid[Int].empty) == 2) assert(Monoid[Int].empty == 0) assert(List.empty[Int].combineAll == 0) assert(List(1, 2, 3, 4).combineAll == 10) val prodMonoid = cats.Monoid.instance[Int](emptyValue = 1, cmb = _ * _) assert(prodMonoid.combine(2, 3) == 6) assert(prodMonoid.combine(3, prodMonoid.empty) == 3) assert(prodMonoid.empty == 1)
  • 74. import cats.Monoid import cats.syntax.monoid.* import cats.syntax.foldable.* 74 assert(Monoid[Int].combine(2,3) == 5) assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2) assert((2 |+| 3) == 5) assert((2 |+| Monoid[Int].empty) == 2) assert(Monoid[Int].empty == 0) assert(List.empty[Int].combineAll == 0) assert(List(1, 2, 3, 4).combineAll == 10) val prodMonoid = cats.Monoid.instance[Int](emptyValue = 1, cmb = _ * _) assert(prodMonoid.combine(2, 3) == 6) assert(prodMonoid.combine(3, prodMonoid.empty) == 3) assert(prodMonoid.empty == 1) assert(List.empty[Int].combineAll(prodMonoid) == 1) assert(List(1, 2, 3, 4).combineAll(prodMonoid) == 24)
  • 75. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.foldLeft(Image.empty)(_ beside _)) .foldLeft(Image.empty)(_ above _) 75
  • 76. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.foldLeft(Image.empty)(_ beside _)) .foldLeft(Image.empty)(_ above _) import cats.Monoid import cats.implicits.* val beside = Monoid.instance[Image](Image.empty, _ beside _) 76
  • 77. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.foldLeft(Image.empty)(_ beside _)) .foldLeft(Image.empty)(_ above _) def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.combineAll(beside)) .foldLeft(Image.empty)(_ above _) import cats.Monoid import cats.implicits.* val beside = Monoid.instance[Image](Image.empty, _ beside _) 77
  • 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
  • 80. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.combineAll(beside)) .foldLeft(Image.empty)(_ above _) 80
  • 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
  • 83. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.combineAll(beside)) .reduce(_ above _) MapReduce is the marketing buzzword for foldMap 83
  • 84. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.combineAll(beside)) .reduce(_ above _) val above = Monoid.instance[Image](Image.empty, _ above _) MapReduce is the marketing buzzword for foldMap 84
  • 85. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.combineAll(beside)) .reduce(_ above _) def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_.combineAll(beside))(above) val above = Monoid.instance[Image](Image.empty, _ above _) MapReduce is the marketing buzzword for foldMap 85
  • 86. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_.combineAll(beside))(above) RECAP 86
  • 87. def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_.fold(beside))(above) fold f o l d M a p fold fold fold 87 beside above
  • 88. def show(queens: List[Int]): String = val lines: List[String] = for (col <- queens.reverse) yield Vector.fill(queens.length)("❎ ") .updated(col, s"πŸ‘‘ ") .mkString "n" + lines.mkString("n") 88 ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎
  • 89. def show(queens: List[Int]): String = val lines: List[String] = for (col <- queens.reverse) yield Vector.fill(queens.length)("❎ ") .updated(col, s"πŸ‘‘ ") .mkString "n" + lines.mkString("n") 89 def show(queens: List[Int]): Image = val squareImageGrid: List[List[Image]] = for col <- queens.reverse yield List.fill(queens.length)(emptySquare) .updated(col,fullSquare) combine(squareImageGrid) ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎
  • 90. def show(queens: List[Int]): String = val lines: List[String] = for (col <- queens.reverse) yield Vector.fill(queens.length)("❎ ") .updated(col, s"πŸ‘‘ ") .mkString "n" + lines.mkString("n") ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ πŸ‘‘ ❎ ❎ 90 def show(queens: List[Int]): Image = val squareImageGrid: List[List[Image]] = for col <- queens.reverse yield List.fill(queens.length)(emptySquare) .updated(col,fullSquare) combine(squareImageGrid)
  • 91. 91
  • 92. We can do a bit more though 92
  • 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
  • 95. def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll beside)(above) 95
  • 96. def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll beside)(above) val on = Monoid.instance[Image](Image.empty, _ on _) 96
  • 97. def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll on)(on) def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll beside)(above) val on = Monoid.instance[Image](Image.empty, _ on _) 97
  • 98. val on = Monoid.instance[Image](Image.empty, _ on _) def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll on)(on) 98
  • 99. given Monoid.instance[Image] = Monoid.instance[Image](Image.empty, _ on _) def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll) val on = Monoid.instance[Image](Image.empty, _ on _) def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll on)(on) 99
  • 100. def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) FULL RECAP 100
  • 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
  • 106. List( List(1, 1, 1, 1), List(1, 1, 1, 2), List(1, 1, 1, 3), List(1, 1, 1, 4), List(1, 1, 2, 1), List(1, 1, 2, 2), List(1, 1, 2, 3), List(1, 1, 2, 4), List(1, 1, 3, 1), List(1, 1, 3, 2), List(1, 1, 3, 3), List(1, 1, 3, 4), List(1, 1, 4, 1), List(1, 1, 4, 2), List(1, 1, 4, 3), List(1, 1, 4, 4), List(1, 2, 1, 1), List(1, 2, 1, 2), List(1, 2, 1, 3), List(1, 2, 1, 4), List(1, 2, 2, 1), List(1, 2, 2, 2), List(1, 2, 2, 3), List(1, 2, 2, 4), List(1, 2, 3, 1), List(1, 2, 3, 2), List(1, 2, 3, 3), List(1, 2, 3, 4), List(1, 2, 4, 1), List(1, 2, 4, 2), List(1, 2, 4, 3), List(1, 2, 4, 4), List(1, 3, 1, 1), List(1, 3, 1, 2), List(1, 3, 1, 3), List(1, 3, 1, 4), List(1, 3, 2, 1), List(1, 3, 2, 2), List(1, 3, 2, 3), List(1, 3, 2, 4), List(1, 3, 3, 1), List(1, 3, 3, 2), List(1, 3, 3, 3), List(1, 3, 3, 4), List(1, 3, 4, 1), List(1, 3, 4, 2), List(1, 3, 4, 3), List(1, 3, 4, 4), List(1, 4, 1, 1), List(1, 4, 1, 2), List(1, 4, 1, 3), List(1, 4, 1, 4), List(1, 4, 2, 1), List(1, 4, 2, 2), List(1, 4, 2, 3), List(1, 4, 2, 4), List(1, 4, 3, 1), List(1, 4, 3, 2), List(1, 4, 3, 3), List(1, 4, 3, 4), List(1, 4, 4, 1), List(1, 4, 4, 2), List(1, 4, 4, 3), List(1, 4, 4, 4), List(2, 1, 1, 1), List(2, 1, 1, 2), List(2, 1, 1, 3), List(2, 1, 1, 4), List(2, 1, 2, 1), List(2, 1, 2, 2), List(2, 1, 2, 3), List(2, 1, 2, 4), List(2, 1, 3, 1), List(2, 1, 3, 2), List(2, 1, 3, 3), List(2, 1, 3, 4), List(2, 1, 4, 1), List(2, 1, 4, 2), List(2, 1, 4, 3), List(2, 1, 4, 4), List(2, 2, 1, 1), List(2, 2, 1, 2), List(2, 2, 1, 3), List(2, 2, 1, 4), List(2, 2, 2, 1), List(2, 2, 2, 2), List(2, 2, 2, 3), List(2, 2, 2, 4), List(2, 2, 3, 1), List(2, 2, 3, 2), List(2, 2, 3, 3), List(2, 2, 3, 4), List(2, 2, 4, 1), List(2, 2, 4, 2), List(2, 2, 4, 3), List(2, 2, 4, 4), List(2, 3, 1, 1), List(2, 3, 1, 2), List(2, 3, 1, 3), List(2, 3, 1, 4), List(2, 3, 2, 1), List(2, 3, 2, 2), List(2, 3, 2, 3), List(2, 3, 2, 4), List(2, 3, 3, 1), List(2, 3, 3, 2), List(2, 3, 3, 3), List(2, 3, 3, 4), List(2, 3, 4, 1), List(2, 3, 4, 2), List(2, 3, 4, 3), List(2, 3, 4, 4), List(2, 4, 1, 1), List(2, 4, 1, 2), List(2, 4, 1, 3), List(2, 4, 1, 4), List(2, 4, 2, 1), List(2, 4, 2, 2), List(2, 4, 2, 3), List(2, 4, 2, 4), List(2, 4, 3, 1), List(2, 4, 3, 2), List(2, 4, 3, 3), List(2, 4, 3, 4), List(2, 4, 4, 1), List(2, 4, 4, 2), List(2, 4, 4, 3), List(2, 4, 4, 4), List(3, 1, 1, 1), List(3, 1, 1, 2), List(3, 1, 1, 3), List(3, 1, 1, 4), List(3, 1, 2, 1), List(3, 1, 2, 2), List(3, 1, 2, 3), List(3, 1, 2, 4), List(3, 1, 3, 1), List(3, 1, 3, 2), List(3, 1, 3, 3), List(3, 1, 3, 4), List(3, 1, 4, 1), List(3, 1, 4, 2), List(3, 1, 4, 3), List(3, 1, 4, 4), List(3, 2, 1, 1), List(3, 2, 1, 2), List(3, 2, 1, 3), List(3, 2, 1, 4), List(3, 2, 2, 1), List(3, 2, 2, 2), List(3, 2, 2, 3), List(3, 2, 2, 4), List(3, 2, 3, 1), List(3, 2, 3, 2), List(3, 2, 3, 3), List(3, 2, 3, 4), List(3, 2, 4, 1), List(3, 2, 4, 2), List(3, 2, 4, 3), List(3, 2, 4, 4), List(3, 3, 1, 1), List(3, 3, 1, 2), List(3, 3, 1, 3), List(3, 3, 1, 4), List(3, 3, 2, 1), List(3, 3, 2, 2), List(3, 3, 2, 3), List(3, 3, 2, 4), List(3, 3, 3, 1), List(3, 3, 3, 2), List(3, 3, 3, 3), List(3, 3, 3, 4), List(3, 3, 4, 1), List(3, 3, 4, 2), List(3, 3, 4, 3), List(3, 3, 4, 4), List(3, 4, 1, 1), List(3, 4, 1, 2), List(3, 4, 1, 3), List(3, 4, 1, 4), List(3, 4, 2, 1), List(3, 4, 2, 2), List(3, 4, 2, 3), List(3, 4, 2, 4), List(3, 4, 3, 1), List(3, 4, 3, 2), List(3, 4, 3, 3), List(3, 4, 3, 4), List(3, 4, 4, 1), List(3, 4, 4, 2), List(3, 4, 4, 3), List(3, 4, 4, 4), List(4, 1, 1, 1), List(4, 1, 1, 2), List(4, 1, 1, 3), List(4, 1, 1, 4), List(4, 1, 2, 1), List(4, 1, 2, 2), List(4, 1, 2, 3), List(4, 1, 2, 4), List(4, 1, 3, 1), List(4, 1, 3, 2), List(4, 1, 3, 3), List(4, 1, 3, 4), List(4, 1, 4, 1), List(4, 1, 4, 2), List(4, 1, 4, 3), List(4, 1, 4, 4), List(4, 2, 1, 1), List(4, 2, 1, 2), List(4, 2, 1, 3), List(4, 2, 1, 4), List(4, 2, 2, 1), List(4, 2, 2, 2), List(4, 2, 2, 3), List(4, 2, 2, 4), List(4, 2, 3, 1), List(4, 2, 3, 2), List(4, 2, 3, 3), List(4, 2, 3, 4), List(4, 2, 4, 1), List(4, 2, 4, 2), List(4, 2, 4, 3), List(4, 2, 4, 4), List(4, 3, 1, 1), List(4, 3, 1, 2), List(4, 3, 1, 3), List(4, 3, 1, 4), List(4, 3, 2, 1), List(4, 3, 2, 2), List(4, 3, 2, 3), List(4, 3, 2, 4), List(4, 3, 3, 1), List(4, 3, 3, 2), List(4, 3, 3, 3), List(4, 3, 3, 4), List(4, 3, 4, 1), List(4, 3, 4, 2), List(4, 3, 4, 3), List(4, 3, 4, 4), List(4, 4, 1, 1), List(4, 4, 1, 2), List(4, 4, 1, 3), List(4, 4, 1, 4), List(4, 4, 2, 1), List(4, 4, 2, 2), List(4, 4, 2, 3), List(4, 4, 2, 4), List(4, 4, 3, 1), List(4, 4, 3, 2), List(4, 4, 3, 3), List(4, 4, 3, 4), List(4, 4, 4, 1), List(4, 4, 4, 2), List(4, 4, 4, 3), List(4, 4, 4, 4)) 106
  • 107. 107 # of results = 256 (size of result) # of permutations with repetition allowed = 256 (44) # of permutations with repetition disallowed = 24 (4!) 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 println(s"# of results = ${results.size}") println(s"# of permutations with repetition allowed = ${4 * 4 * 4 * 4}") // NN println(s"# of permutations with repetition disallowed = ${4 * 3 * 2 * 1}") // N!
  • 108. N = 4 # of permutations = 44 = 256 108
  • 109. N = 4 # of permutations = 44 = 256 How many of these are solutions? 109
  • 110. N N-queens solution count 1 1 2 0 3 0 4 2 5 10 6 4 7 40 8 92 9 352 10 724 11 2,680 12 14,200 13 73,712 14 365,596 15 2,279,184 16 14,772,512 17 95,815,104 18 666,090,624 19 4,968,057,848 20 39,029,188,884 21 314,666,222,712 22 2,691,008,701,644 23 24,233,937,684,440 24 227,514,171,973,736 25 2,207,893,435,808,352 26 22,317,699,616,364,044 27 234,907,967,154,122,528 data source: https://en.wikipedia.org/wiki/Eight_queens_puzzle 110
  • 111. N = 4 # of permutations = 44 = 256 # of solutions = 2 111
  • 112. How do we find the solutions? 112
  • 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
  • 128. List( List(1, 1, 1, 1), List(1, 1, 1, 2), List(1, 1, 1, 3), List(1, 1, 1, 4), List(1, 1, 2, 1), List(1, 1, 2, 2), List(1, 1, 2, 3), List(1, 1, 2, 4), List(1, 1, 3, 1), List(1, 1, 3, 2), List(1, 1, 3, 3), List(1, 1, 3, 4), List(1, 1, 4, 1), List(1, 1, 4, 2), List(1, 1, 4, 3), List(1, 1, 4, 4), List(1, 2, 1, 1), List(1, 2, 1, 2), List(1, 2, 1, 3), List(1, 2, 1, 4), List(1, 2, 2, 1), List(1, 2, 2, 2), List(1, 2, 2, 3), List(1, 2, 2, 4), List(1, 2, 3, 1), List(1, 2, 3, 2), List(1, 2, 3, 3), List(1, 2, 3, 4), List(1, 2, 4, 1), List(1, 2, 4, 2), List(1, 2, 4, 3), List(1, 2, 4, 4), List(1, 3, 1, 1), List(1, 3, 1, 2), List(1, 3, 1, 3), List(1, 3, 1, 4), List(1, 3, 2, 1), List(1, 3, 2, 2), List(1, 3, 2, 3), List(1, 3, 2, 4), List(1, 3, 3, 1), List(1, 3, 3, 2), List(1, 3, 3, 3), List(1, 3, 3, 4), List(1, 3, 4, 1), List(1, 3, 4, 2), List(1, 3, 4, 3), List(1, 3, 4, 4), List(1, 4, 1, 1), List(1, 4, 1, 2), List(1, 4, 1, 3), List(1, 4, 1, 4), List(1, 4, 2, 1), List(1, 4, 2, 2), List(1, 4, 2, 3), List(1, 4, 2, 4), List(1, 4, 3, 1), List(1, 4, 3, 2), List(1, 4, 3, 3), List(1, 4, 3, 4), List(1, 4, 4, 1), List(1, 4, 4, 2), List(1, 4, 4, 3), List(1, 4, 4, 4), List(2, 1, 1, 1), List(2, 1, 1, 2), List(2, 1, 1, 3), List(2, 1, 1, 4), List(2, 1, 2, 1), List(2, 1, 2, 2), List(2, 1, 2, 3), List(2, 1, 2, 4), List(2, 1, 3, 1), List(2, 1, 3, 2), List(2, 1, 3, 3), List(2, 1, 3, 4), List(2, 1, 4, 1), List(2, 1, 4, 2), List(2, 1, 4, 3), List(2, 1, 4, 4), List(2, 2, 1, 1), List(2, 2, 1, 2), List(2, 2, 1, 3), List(2, 2, 1, 4), List(2, 2, 2, 1), List(2, 2, 2, 2), List(2, 2, 2, 3), List(2, 2, 2, 4), List(2, 2, 3, 1), List(2, 2, 3, 2), List(2, 2, 3, 3), List(2, 2, 3, 4), List(2, 2, 4, 1), List(2, 2, 4, 2), List(2, 2, 4, 3), List(2, 2, 4, 4), List(2, 3, 1, 1), List(2, 3, 1, 2), List(2, 3, 1, 3), List(2, 3, 1, 4), List(2, 3, 2, 1), List(2, 3, 2, 2), List(2, 3, 2, 3), List(2, 3, 2, 4), List(2, 3, 3, 1), List(2, 3, 3, 2), List(2, 3, 3, 3), List(2, 3, 3, 4), List(2, 3, 4, 1), List(2, 3, 4, 2), List(2, 3, 4, 3), List(2, 3, 4, 4), List(2, 4, 1, 1), List(2, 4, 1, 2), List(2, 4, 1, 3), List(2, 4, 1, 4), List(2, 4, 2, 1), List(2, 4, 2, 2), List(2, 4, 2, 3), List(2, 4, 2, 4), List(2, 4, 3, 1), List(2, 4, 3, 2), List(2, 4, 3, 3), List(2, 4, 3, 4), List(2, 4, 4, 1), List(2, 4, 4, 2), List(2, 4, 4, 3), List(2, 4, 4, 4), List(3, 1, 1, 1), List(3, 1, 1, 2), List(3, 1, 1, 3), List(3, 1, 1, 4), List(3, 1, 2, 1), List(3, 1, 2, 2), List(3, 1, 2, 3), List(3, 1, 2, 4), List(3, 1, 3, 1), List(3, 1, 3, 2), List(3, 1, 3, 3), List(3, 1, 3, 4), List(3, 1, 4, 1), List(3, 1, 4, 2), List(3, 1, 4, 3), List(3, 1, 4, 4), List(3, 2, 1, 1), List(3, 2, 1, 2), List(3, 2, 1, 3), List(3, 2, 1, 4), List(3, 2, 2, 1), List(3, 2, 2, 2), List(3, 2, 2, 3), List(3, 2, 2, 4), List(3, 2, 3, 1), List(3, 2, 3, 2), List(3, 2, 3, 3), List(3, 2, 3, 4), List(3, 2, 4, 1), List(3, 2, 4, 2), List(3, 2, 4, 3), List(3, 2, 4, 4), List(3, 3, 1, 1), List(3, 3, 1, 2), List(3, 3, 1, 3), List(3, 3, 1, 4), List(3, 3, 2, 1), List(3, 3, 2, 2), List(3, 3, 2, 3), List(3, 3, 2, 4), List(3, 3, 3, 1), List(3, 3, 3, 2), List(3, 3, 3, 3), List(3, 3, 3, 4), List(3, 3, 4, 1), List(3, 3, 4, 2), List(3, 3, 4, 3), List(3, 3, 4, 4), List(3, 4, 1, 1), List(3, 4, 1, 2), List(3, 4, 1, 3), List(3, 4, 1, 4), List(3, 4, 2, 1), List(3, 4, 2, 2), List(3, 4, 2, 3), List(3, 4, 2, 4), List(3, 4, 3, 1), List(3, 4, 3, 2), List(3, 4, 3, 3), List(3, 4, 3, 4), List(3, 4, 4, 1), List(3, 4, 4, 2), List(3, 4, 4, 3), List(3, 4, 4, 4), List(4, 1, 1, 1), List(4, 1, 1, 2), List(4, 1, 1, 3), List(4, 1, 1, 4), List(4, 1, 2, 1), List(4, 1, 2, 2), List(4, 1, 2, 3), List(4, 1, 2, 4), List(4, 1, 3, 1), List(4, 1, 3, 2), List(4, 1, 3, 3), List(4, 1, 3, 4), List(4, 1, 4, 1), List(4, 1, 4, 2), List(4, 1, 4, 3), List(4, 1, 4, 4), List(4, 2, 1, 1), List(4, 2, 1, 2), List(4, 2, 1, 3), List(4, 2, 1, 4), List(4, 2, 2, 1), List(4, 2, 2, 2), List(4, 2, 2, 3), List(4, 2, 2, 4), List(4, 2, 3, 1), List(4, 2, 3, 2), List(4, 2, 3, 3), List(4, 2, 3, 4), List(4, 2, 4, 1), List(4, 2, 4, 2), List(4, 2, 4, 3), List(4, 2, 4, 4), List(4, 3, 1, 1), List(4, 3, 1, 2), List(4, 3, 1, 3), List(4, 3, 1, 4), List(4, 3, 2, 1), List(4, 3, 2, 2), List(4, 3, 2, 3), List(4, 3, 2, 4), List(4, 3, 3, 1), List(4, 3, 3, 2), List(4, 3, 3, 3), List(4, 3, 3, 4), List(4, 3, 4, 1), List(4, 3, 4, 2), List(4, 3, 4, 3), List(4, 3, 4, 4), List(4, 4, 1, 1), List(4, 4, 1, 2), List(4, 4, 1, 3), List(4, 4, 1, 4), List(4, 4, 2, 1), List(4, 4, 2, 2), List(4, 4, 2, 3), List(4, 4, 2, 4), List(4, 4, 3, 1), List(4, 4, 3, 2), List(4, 4, 3, 3), List(4, 4, 3, 4), List(4, 4, 4, 1), List(4, 4, 4, 2), List(4, 4, 4, 3), List(4, 4, 4, 4)) List( List(3,1,4,2), List(2,4,1,3)) ) BEFORE AFTER adding filtering (isSafe function) 128
  • 129. BEFORE AFTER adding filtering (isSafe function) 129
  • 130. Let’s visualise how the filtering reduces the problem space 130
  • 131. 131
  • 132. 132
  • 133. 133
  • 134. 134
  • 135. Candidate boards generated and tested: 60 (out of 256) 135
  • 136. N = 4 2 solutions 136
  • 137. N = 5 10 solutions 137
  • 138. N = 6 4 solutions 138
  • 139. N = 7 40 solutions 139
  • 140. N = 8 92 solutions 140
  • 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
  • 150. map :: (a -> b) -> [a] -> [b] 150
  • 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
  • 154. Paul Chiusano Runar Bjarnason @pchiusano @runarorama FP in Scala There turns out to be a startling number of operations that can be defined in the most general possible way in terms of sequence and/or traverse 154
  • 155. A quick refresher on the traverse function 155
  • 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.
  • 157. scala> import scala.concurrent.Future | import concurrent.ExecutionContext.Implicits.global 157
  • 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 =