Slide 1

Slide 1 text

𝑚𝑎𝑝𝑀 monadic mapping, filtering, folding 𝑓𝑖𝑙𝑡𝑒𝑟𝑀 𝑓𝑜𝑙𝑑𝑀 N-Queens Combinatorial Puzzle meetsCats monoidal functions 𝑓𝑜𝑙𝑑 and 𝑓𝑜𝑙𝑑𝑀𝑎𝑝

Slide 2

Slide 2 text

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

Slide 4

Slide 4 text

An 8 x 8 chessboard has 64 cells. How many ways are there of placing 8 queens on the board? 4

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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 conﬁguraPon 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

Slide 11

Slide 11 text

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 conﬁguraPon 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

Slide 12

Slide 12 text

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 conﬁguraPon 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

Slide 13

Slide 13 text

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 conﬁguraPon 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

Slide 14

Slide 14 text

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 conﬁguraPon

Slide 15

Slide 15 text

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 conﬁguraPon 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

Slide 16

Slide 16 text

Let’s try to find a solution to the puzzle 16

17

18

19

20

21

22

Slide 23

Slide 23 text

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.

Slide 24

Slide 24 text

Let’s try again 24

25

26

27

28

29

30

31

32

33

Slide 34

Slide 34 text

34 We found a solution

Slide 35

Slide 35 text

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

Slide 37

Slide 37 text

(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

Slide 38

Slide 38 text

(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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Compositional Vector Graphics 44

Slide 45

Slide 45 text

val redSquare = Image.square(100).fillColor(Color.red) 45

Slide 46

Slide 46 text

val redSquare = Image.square(100).fillColor(Color.red) val blueSquare = Image.square(100).fillColor(Color.blue) 46

Slide 47

Slide 47 text

val redSquare = Image.square(100).fillColor(Color.red) val blueSquare = Image.square(100).fillColor(Color.blue) val redBesideBlue = redSquare.beside(blueSquare) 47

Slide 48

Slide 48 text

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

Slide 50

Slide 50 text

create Solution red and white square images 50 Image.square Image.fillColor

Slide 51

Slide 51 text

create Solution red and white square images compose composite solution image 51 Image.beside Image.above Image.square Image.fillColor

Slide 52

Slide 52 text

val square: Image = Image.square(100).strokeColor(Color.black) val emptySquare: Image = square.fillColor(Color.white) val fullSquare: Image = square.fillColor(Color.orangeRed) 52

Slide 53

Slide 53 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) 53

Slide 54

Slide 54 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) m a p 54

Slide 55

Slide 55 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) reduce m a p reduce reduce reduce 55 beside

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) 57

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 67

Slide 67 text

import cats.Monoid 67 assert(Monoid[Int].combine(2,3) == 5) assert(Monoid[Int].combine(2, Monoid[Int].empty) == 2)

Slide 68

Slide 68 text

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)

Slide 69

Slide 69 text

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)

Slide 70

Slide 70 text

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) … }

Slide 71

Slide 71 text

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 = _ * _)

Slide 72

Slide 72 text

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)

Slide 73

Slide 73 text

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)

Slide 74

Slide 74 text

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)

Slide 75

Slide 75 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.foldLeft(Image.empty)(_ beside _)) .foldLeft(Image.empty)(_ above _) 75

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.combineAll(beside)) .foldLeft(Image.empty)(_ above _) 80

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.combineAll(beside)) .reduce(_ above _) MapReduce is the marketing buzzword for foldMap 83

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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 ❎ ❎ ❎ 👑 ❎ ❎ ❎ ❎ 👑 ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ 👑 ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ 👑 ❎ 👑 ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ 👑 ❎ ❎ ❎ 👑 ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ 👑 ❎ ❎

Slide 89

Slide 89 text

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) ❎ ❎ ❎ 👑 ❎ ❎ ❎ ❎ 👑 ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ 👑 ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ 👑 ❎ 👑 ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ 👑 ❎ ❎ ❎ 👑 ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ ❎ 👑 ❎ ❎

Slide 90

Slide 90 text

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

Slide 92

Slide 92 text

We can do a bit more though 92

Slide 93

Slide 93 text

93 The images that we have been creating up to now have been centered on the origin of the coordinate system

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll beside)(above) 95

Slide 96

Slide 96 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll beside)(above) val on = Monoid.instance[Image](Image.empty, _ on _) 96

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

val on = Monoid.instance[Image](Image.empty, _ on _) def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll on)(on) 98

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

def combine(imageGrid: List[List[Image]]): Image = imageGrid .map(_.reduce(_ beside _)) .reduce(_ above _) FULL RECAP 100

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

How do we compute the permutations of N queens, e.g. for N=4? 104

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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!

Slide 108

Slide 108 text

N = 4 # of permutations = 44 = 256 108

Slide 109

Slide 109 text

N = 4 # of permutations = 44 = 256 How many of these are solutions? 109

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

N = 4 # of permutations = 44 = 256 # of solutions = 2 111

Slide 112

Slide 112 text

How do we find the solutions? 112

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

N = 4 # of permutations = 44 = 256 Same result as before using recursion 122

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

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

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

BEFORE AFTER adding filtering (isSafe function) 129

Slide 130

Slide 130 text

Let’s visualise how the filtering reduces the problem space 130

131

132

133

134

Slide 135

Slide 135 text

Candidate boards generated and tested: 60 (out of 256) 135

Slide 136

Slide 136 text

N = 4 2 solutions 136

Slide 137

Slide 137 text

N = 5 10 solutions 137

Slide 138

Slide 138 text

N = 6 4 solutions 138

Slide 139

Slide 139 text

N = 7 40 solutions 139

Slide 140

Slide 140 text

N = 8 92 solutions 140

Slide 141

Slide 141 text

92 boards N = 5 N = 6 N = 4 N = 7 N = 8 40 boards 4 boards 10 boards 2 boards

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

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

Slide 144

Slide 144 text

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

Slide 145

Slide 145 text

We are all very familiar with the 3 functions that are the bread, butter, and jam of Functional Programming 145

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

map λ mapM λ Let’s gain an understanding of the foldM function by looking at the monadic variant of the triad 147

λ mapM 148

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

map :: (a -> b) -> [a] -> [b] 150

Slide 151

Slide 151 text

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

Slide 152

Slide 152 text

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

Slide 154

Slide 154 text

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

Slide 155

Slide 155 text

A quick refresher on the traverse function 155

Slide 156

Slide 156 text

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.

Slide 157

Slide 157 text

scala> import scala.concurrent.Future | import concurrent.ExecutionContext.Implicits.global 157

Slide 158

Slide 158 text

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

Slide 159

Slide 159 text

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

Slide 160

Slide 160 text

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

Slide 161

Slide 161 text

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

Slide 162

Slide 162 text

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

Slide 163

Slide 163 text

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

Slide 164

Slide 164 text

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

Slide 165

Slide 165 text

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

Slide 166

Slide 166 text

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

Slide 167

Slide 167 text

That was the quick refresher on the traverse function 167

Slide 168

Slide 168 text

class (Functor t, Foldable t) => Traversable t where traverse :: Applicative f => (a -> f b) -> t a -> f (t b) 168

Slide 169

Slide 169 text

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

Slide 170

Slide 170 text

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

Slide 171

Slide 171 text

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

Slide 172

Slide 172 text

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

Slide 173

Slide 173 text

/** * 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

Slide 174

Slide 174 text

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

Slide 175

Slide 175 text

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…

Slide 176

Slide 176 text

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…

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

A quick mention of the Symmetry that exists in the interrelation of flatMap/foldMap/traverse and flatten/fold/sequence 178

Slide 179

Slide 179 text

def flatMap[A,B](ma: F[A])(f: A ⇒ F[B]): F[B] = flatten(map(ma)(f)) 179

Slide 180

Slide 180 text

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

Slide 181

Slide 181 text

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

Slide 182

Slide 182 text

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

Slide 183

Slide 183 text

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

Slide 184

Slide 184 text

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

Slide 185

Slide 185 text

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

Slide 186

Slide 186 text

Examples of mapM usage Example Monadic Context How the result of mapM is affected 1 2 3 186

Slide 187

Slide 187 text

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

Slide 188

Slide 188 text

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

Slide 189

Slide 189 text

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

Slide 190

Slide 190 text

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

Slide 191

Slide 191 text

• 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

Slide 192

Slide 192 text

• 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

Slide 193

Slide 193 text

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

Slide 194

Slide 194 text

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

Slide 195

Slide 195 text

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

Slide 196

Slide 196 text

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

Slide 197

Slide 197 text

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

Slide 198

Slide 198 text

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

Slide 199

Slide 199 text

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

Slide 200

Slide 200 text

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

Slide 201

Slide 201 text

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")

Slide 202

Slide 202 text

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")

Slide 203

Slide 203 text

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")

Slide 204

Slide 204 text

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")

Slide 205

Slide 205 text

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()

Slide 206

Slide 206 text

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()

Slide 207

Slide 207 text

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()

Slide 208

Slide 208 text

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")

Slide 209

Slide 209 text

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")

Slide 210

Slide 210 text

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")

Slide 211

Slide 211 text

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")

Slide 212

Slide 212 text

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")

Slide 213

Slide 213 text

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")

Slide 214

Slide 214 text

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()

Slide 215

Slide 215 text

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()

Slide 216

Slide 216 text

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

Slide 217

Slide 217 text

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

Slide 218

Slide 218 text

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

Slide 219

Slide 219 text

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

Slide 220

Slide 220 text

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

Slide 221

Slide 221 text

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

Slide 222

Slide 222 text

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

Slide 223

Slide 223 text

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

Slide 224

Slide 224 text

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

Slide 225

Slide 225 text

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

Slide 226

Slide 226 text

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

Slide 227

Slide 227 text

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

Slide 228

Slide 228 text

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

Slide 229

Slide 229 text

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

Slide 230

Slide 230 text

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

Slide 231

Slide 231 text

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: ") val getNumbers: IO[List[Int]] = messages.mapM{ getNumber } 231 side effects suspended in pure value

Slide 232

Slide 232 text

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: ") val getNumbers: IO[List[Int]] = messages.mapM{ getNumber } val numbers: List[Int] = getNumbers.unsafeRunSync() Enter a number: 3 Enter another: 2 232 side effects occur

Slide 233

Slide 233 text

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: ") val getNumbers: IO[List[Int]] = messages.mapM{ getNumber } val numbers: List[Int] = getNumbers.unsafeRunSync() println(s"The two numbers add up to \${numbers.sum}") Enter a number: 3 Enter another: 2 The two numbers add up to 5 233

Slide 234

Slide 234 text

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: ") val getNumbers: IO[List[Int]] = messages.mapM{ getNumber } val numbers: List[Int] = getNumbers.unsafeRunSync() println(s"The two numbers add up to \${numbers.sum}") if getNumber cannot parse an input, it returns 0 234 Enter a number: 3 Enter another: a The two numbers add up to 3

Slide 235

Slide 235 text

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: ") val getNumbers: IO[List[Int]] = messages.mapM{ getNumber } val numbers: List[Int] = getNumbers.unsafeRunSync() println(s"The two numbers add up to \${numbers.sum}") 235 If IO.readLine hangs waiting for input, so does the whole program Enter a number: ⏳…

Slide 236

Slide 236 text

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: ") val getNumbers: IO[List[Int]] = messages.mapM{ getNumber } val numbers: List[Int] = getNumbers.unsafeRunSync() println(s"The two numbers add up to \${numbers.sum}") 236 If IO.readLine throws an exception, so does the whole program Enter a number: ⚡⚡⚡

mapM λ 237

mapM λ 238

mapM λ 239

Slide 240

Slide 240 text

A monadic version of the filter function on lists is defined by generalizing its type and definition in a similar manner to mapM: filterM :: Monad m => (a -> m Bool) -> [a] -> m [a] filterM p [] = return [] filterM p (x:xs) = do b <- p x ys <- filterM p xs return (if b then x:ys else ys) Graham Hutton @haskellhutt 240

Slide 241

Slide 241 text

import cats.Monad import cats.syntax.functor.* // map import cats.syntax.flatMap.* // flatMap import cats.syntax.applicative.* // pure def filterM[A, M[_]: Monad](l: List[A])(f: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for retainingA <- p(a) retainedAs <- filterM(as)(p) yield if retainingA then a::retainedAs else retainedAs filterM :: Monad m => (a -> m Bool) -> [a] -> m [a] filterM p [] = return [] filterM p (x:xs) = do b <- p x ys <- filterM p xs return (if b then x:ys else ys) Cats filter :: (a -> Bool) -> [a] -> [a] 241

Slide 242

Slide 242 text

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 Cats import cats.Monad import cats.syntax.functor.* // map import cats.syntax.flatMap.* // flatMap import cats.syntax.applicative.* // pure def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for retainingA <- p(a) retainedAs <- filterM(as)(p) yield if retainingA then a::retainedAs else retainedAs mapM filterM 242

Slide 243

Slide 243 text

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 Cats import cats.Monad import cats.syntax.functor.* // map import cats.syntax.flatMap.* // flatMap import cats.syntax.applicative.* // pure def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for retainingA <- p(a) retainedAs <- filterM(as)(p) yield if retainingA then a::retainedAs else retainedAs mapM filterM 243

Slide 244

Slide 244 text

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 Cats import cats.Monad import cats.syntax.functor.* // map import cats.syntax.flatMap.* // flatMap import cats.syntax.applicative.* // pure def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for retainingA <- p(a) retainedAs <- filterM(as)(p) yield if retainingA then a::retainedAs else retainedAs mapM filterM 244

Slide 245

Slide 245 text

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 Cats import cats.Monad import cats.syntax.functor.* // map import cats.syntax.flatMap.* // flatMap import cats.syntax.applicative.* // pure def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for retainingA <- p(a) retainedAs <- filterM(as)(p) yield if retainingA then a::retainedAs else retainedAs mapM filterM 245

Slide 246

Slide 246 text

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 Cats import cats.Monad import cats.syntax.functor.* // map import cats.syntax.flatMap.* // flatMap import cats.syntax.applicative.* // pure def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for retainingA <- p(a) retainedAs <- filterM(as)(p) yield if retainingA then a::retainedAs else retainedAs mapM filterM 246

filterM ? 247

Slide 248

Slide 248 text

https://typelevel.org/cats/api/cats/TraverseFilter.html 248

Slide 249

Slide 249 text

import cats.Monad import cats.syntax.traverseFilter.* extension[A, M[_]: Monad](as: List[A]) def filterM(f: A => M[Boolean]): M[List[A]] = as.filterA(f) def filterM [A, M[_]: Monad](as: List[A])(f: A => M[Boolean]): M[List[A]] filterM :: Monad m => (a -> m Bool) -> [a] -> m [a] 249

Slide 250

Slide 250 text

Examples of filterM usage Example Monadic Context 1 2 250

Slide 251

Slide 251 text

Examples of filterM usage Example Monadic Context 1 Option 2 251

Slide 252

Slide 252 text

conv :: Char -> Maybe Int conv c | isDigit c = Just (digitToInt c) | otherwise = Nothing > mapM conv "1234" Just [1,2,3,4] > mapM conv "123a" Nothing def convert(c: Char): Option[Int] = Option.when(c.isDigit)(c.asDigit) assert( "1234".toList.mapM(convert) == Some(List(1, 2, 3, 4)) ) assert( "1234a".toList.mapM(convert) == None ) 252 conv function returns Int in an Option monadic context

Slide 253

Slide 253 text

conv :: Char -> Maybe Int conv c | isDigit c = Just (digitToInt c) | otherwise = Nothing > mapM conv "1234" Just [1,2,3,4] > mapM conv "123a" Nothing maybeIsEven :: Char -> Maybe Bool maybeIsEven c | isDigit c = Just (even (digitToInt c)) | otherwise = Nothing > filterM maybeIsEven "1234" Just [2,4] > mapM maybeIsEven "123a" Nothing def conv(c: Char): Option[Int] = Option.when(c.isDigit)(c.asDigit) assert( "12a4".toList.mapM(conv) == None ) assert( "1234".toList.mapM(conv) == Some(List(1, 2, 3, 4)) ) def maybeIsEven(c: Char): Option[Boolean] = Option.when(c.isDigit)(c.asDigit % 2 == 0) assert( "12a4".toList.filterM(maybeIsEven) == None ) assert( "1234".toList.filterM(maybeIsEven) == Some(List('2','4’)) ) 253 conv function returns Int in an Option monadic context maybeIsEven function returns a Boolean in an Option monadic context

Slide 254

Slide 254 text

Examples of filterM usage Example Monadic Context 1 Option 2 254

Slide 255

Slide 255 text

Examples of filterM usage Example Monadic Context 1 Option 2 List 255

Slide 256

Slide 256 text

For example, in the case of the list monad, using filterM provides a particularly concise means of computing the powerset of a list, which is given by all possible ways of including or excluding each element of the list: > filterM (\x -> [True,False]) [1,2,3] [[1,2,3],[1,2],[1,3],[1],[2,3],[2],[3],[]] Graham Hutton @haskellhutt assert( List(1, 2, 3).filterM(_ => List(true, false)) == List(List(1, 2, 3), List(1, 2), List(1, 3), List(1), List(2, 3), List(2), List(3), List()) ) 256 the anonymous function returns a Boolean in a List monadic context

Slide 257

Slide 257 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) filteredAs <- filterM(as)(p) yield if keepingA then a::filteredAs else filteredAs [] List().filterM(_ => List(true, false)) 257

Slide 258

Slide 258 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) filteredAs <- filterM(as)(p) yield if keepingA then a::filteredAs else filteredAs [] [[]] List().filterM(_ => List(true, false)) 258

Slide 259

Slide 259 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List()) yield if keepingA then a::filteredAs else filteredAs [] [3] 3:: [[]] List(3).filterM(_ => List(true, false)) 259

Slide 260

Slide 260 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] 3:: [[]] List(3).filterM(_ => List(true, false)) 260

Slide 261

Slide 261 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] 3:: [[]] [[3],[]] List(3).filterM(_ => List(true, false)) 261

Slide 262

Slide 262 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] 2: 3: [[]] [[3],[]] List(2, 3).filterM(_ => List(true, false)) 262

Slide 263

Slide 263 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [2] 2: 2: 3: [[]] [[3],[]] List(2, 3).filterM(_ => List(true, false)) 263

Slide 264

Slide 264 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] 2: 3: [[]] [[3],[]] List(2, 3).filterM(_ => List(true, false)) 264 [2] 2:

Slide 265

Slide 265 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] 2: 2: 3: [[]] [[3],[]] List(2, 3).filterM(_ => List(true, false)) 265

Slide 266

Slide 266 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] List(2, 3).filterM(_ => List(true, false)) 266

Slide 267

Slide 267 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(2,3),List(2),List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] [1,2,3] 1: 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] List(1, 2, 3).filterM(_ => List(true, false)) 267

Slide 268

Slide 268 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(2,3),List(2),List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] [1,2,3] 1: 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] List(1, 2, 3).filterM(_ => List(true, false)) 268 [1,2] 1:

Slide 269

Slide 269 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(2,3),List(2),List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] [1,2,3] [1,3] [1,2] 1: 1: 1: 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] List(1, 2, 3).filterM(_ => List(true, false)) 269

Slide 270

Slide 270 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(2,3),List(2),List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] [1,2,3] [1,3] [1,2] [1] 1: 1: 1: 1: 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] List(1, 2, 3).filterM(_ => List(true, false)) 270

Slide 271

Slide 271 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(2,3),List(2),List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] [1,2,3] [2,3] 3: 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] List(1, 2, 3).filterM(_ => List(true, false)) 271 [1,3] [1,2] [1] 1: 1: 1:

Slide 272

Slide 272 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(2,3),List(2),List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] [1,2,3] [2,3] [1,3] [1,2] [2] 1: 1: 1: 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] List(1, 2, 3).filterM(_ => List(true, false)) 272 [1] 1:

Slide 273

Slide 273 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(2,3),List(2),List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] [1,2,3] [2,3] [1,3] [3] 1: 1: 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] List(1, 2, 3).filterM(_ => List(true, false)) 273 [1,2] [1] 1: 1: [2]

Slide 274

Slide 274 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) // List(true, false) filteredAs <- filterM(as)(p) // List(List(2,3),List(2),List(3),List()) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] [1,2,3] [2,3] [1,3] [3] [1,2] [2] [1] [] 1: 1: 1: 1: 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] List(1, 2, 3).filterM(_ => List(true, false)) 274

Slide 275

Slide 275 text

def filterM[A, M[_]: Monad](l: List[A])(p: A => M[Boolean]): M[List[B]] = l match case Nil => Nil.pure case a::as => for keepingA <- p(a) filteredAs <- filterM(as)(p) yield if keepingA then a::filteredAs else filteredAs [] [3] [] [2,3] [3] [2] [] [1,2,3] [2,3] [1,3] [3] [1,2] [2] [1] [] 1: 1: 1: 1: 2: 2: 3: [[]] [[3],[]] [[2,3],[2],[3],[]] [[1,2,3],[1,2],[1,3],[1],[2,3],[2],[3],[]] List(1, 2, 3).filterM(_ => List(true, false)) 275

mapM λ 276

mapM λ 277

mapM λ 278

Slide 279

Slide 279 text

foldl :: (a -> b -> a) -> a -> [b] -> a def foldLeft[B](z: B)(op: (B, A) => B): B Applies a binary operator to a start value and all elements of this collection, going left to right. 279

Slide 280

Slide 280 text

Miran Lipovača Let’s sum a list of numbers with a fold: > foldl (\acc x -> acc + x) 0 [2,8,3,1] 14 … Now what if we wanted to sum a list of numbers but with the added condition that if any number is greater than 9 in the list, the whole thing fails? … 280

Slide 281

Slide 281 text

Miran Lipovača foldM The monadic counterpart to foldl is foldM. … The type of foldl is this: foldl :: (a -> b -> a) -> a -> [b] -> a Whereas foldM has the following type: foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a The value that the binary function returns is monadic and so the result of the whole fold is monadic as well. 281

Slide 282

Slide 282 text

Miran Lipovača foldM The monadic counterpart to foldl is foldM. … The type of foldl is this: foldl :: (a -> b -> a) -> a -> [b] -> a Whereas foldM has the following type: foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a The value that the binary function returns is monadic and so the result of the whole fold is monadic as well. 282

Slide 284

Slide 284 text

Examples of foldM usage: Example Monadic Context How the result of foldM is affected 1 2 284

Slide 285

Slide 285 text

Examples of foldM usage: Example Monadic Context How the result of foldM is affected 1 Option There may or may not be a result. 2 285

Slide 286

Slide 286 text

binSmalls :: Int -> Int -> Maybe Int binSmalls acc x | x > 9 = Nothing | otherwise = Just (acc + x) ghci> foldM binSmalls 0 [2,8,3,1] Just 14 ghci> foldM binSmalls 0 [2,11,3,1] Nothing Excellent! Because one number in the list was greater than 9, the whole thing resulted in a Nothing. Miran Lipovača def foldM[G[_], B](z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] import cats.syntax.foldable._ assert( List(2,11,3,1).foldM(0)(binSmalls) == None ) assert( List(2,8,3,1).foldM(0)(binSmalls) == Some(14) ) def binSmalls(acc: Int, x: Int): Option[Int] = x match case x if x > 9 => None case otherwise => Some(acc + x) 286

Slide 287

Slide 287 text

That example of using foldM with a binary function that returns an optional value is useful. 287

Slide 288

Slide 288 text

That example of using foldM with a binary function that returns an optional value is useful. Things get a bit harder to understand when the binary function returns a list of values. 288

Slide 289

Slide 289 text

That example of using foldM with a binary function that returns an optional value is useful. Things get a bit harder to understand when the binary function returns a list of values. The way we are going to solve the N-Queens puzzle using foldM is by passing the latter a binary function returning a list of values. 289

Slide 290

Slide 290 text

That example of using foldM with a binary function that returns an optional value is useful. Things get a bit harder to understand when the binary function returns a list of values. The way we are going to solve the N-Queens puzzle using foldM is by passing the latter a binary function returning a list of values. So in upcoming slides we are going to look at a number of examples that do just that. 290

Slide 291

Slide 291 text

That example of using foldM with a binary function that returns an optional value is useful. Things get a bit harder to understand when the binary function returns a list of values. The way we are going to solve the N-Queens puzzle using foldM is by passing the latter a binary function returning a list of values. So in upcoming slides we are going to look at a number of examples that do just that. This is to strengthen our understanding of the foldM function. 291

Slide 292

Slide 292 text

That example of using foldM with a binary function that returns an optional value is useful. Things get a bit harder to understand when the binary function returns a list of values. The way we are going to solve the N-Queens puzzle using foldM is by passing the latter a binary function returning a list of values. So in upcoming slides we are going to look at a number of examples that do just that. This is to strengthen our understanding of the foldM function. Before we do that though, let’s take another look at the definition of foldM. 292

Slide 293

Slide 293 text

https://hackage.haskell.org/package/base-4.15.0.0/docs/Control-Monad.html foldM f a1 [x1, x2, ..., xm] == do a2 <- f a1 x1 -- access the value that is wrapped in a monadic context a3 <- f a2 x2 -- access the value that is wrapped in a monadic context ... f am xm – return the value leaving it wrapped in a monadic context 293

Slide 294

Slide 294 text

foldM f a1 [x1, x2, ..., xm] == do a2 <- f a1 x1 a3 <- f a2 x2 ... f am xm 294

Slide 295

Slide 295 text

foldM f a1 [x1, x2, ..., xm] == do a2 <- f a1 x1 a3 <- f a2 x2 ... f am xm foldM f a1 [x1, x2, ..., xm] == do a2 <- f a1 x1 a3 <- f a2 x2 ... return f am xm we can look at the above as follows 295

Slide 296

Slide 296 text

foldM f a1 [x1, x2, ..., xm] == do a2 <- f a1 x1 a3 <- f a2 x2 ... f am xm List(x1, x2, ..., xm).foldM(a1)(f) == (for a2 <- f(a1,x1) a3 <- f(a2,x2) ... yield f(am,xm)).flatten foldM f a1 [x1, x2, ..., xm] == do a2 <- f a1 x1 a3 <- f a2 x2 ... return f am xm we can look at the above as follows which translates to 296

Slide 297

Slide 297 text

Examples of foldM usage: Example Monadic Context How the result of foldM is affected 1 Option There may or may not be a result. 2 297

Slide 298

Slide 298 text

Examples of foldM usage: Example Monadic Context How the result of foldM is affected 1 Option There may or may not be a result. 2 List There may be zero, one, or more results. So foldM manages zero, one, or more accumulators. 298

Slide 299

Slide 299 text

In the following examples, the following property holds… If foldM is applied to • a list of length n • an initial accumulator z • a function returning a list of length m Then foldM returns a list of length m ^ n. assert( List(...).foldM(...)((acc, val) => List(...)) == List(...) ) m=? z n=? m^n=? 299

Slide 300

Slide 300 text

In the following examples, the following property holds… If foldM is applied to • a list of length n • an initial accumulator z • a function returning a list of length m Then foldM returns a list of length m ^ n. assert( List(...).foldM(...)((acc, val) => List(...)) == List(...) ) m=? z n=? m^n=? assert( List().foldM(...)((acc, val) => List(...)) == List(z) ) m=? z n=0 m^n=1 when n=0, the returned list is a singleton containing z 300

Slide 301

Slide 301 text

assert( List().foldM(9)((_, _) => List(0, 0)) == List(9) ) m=2 z n=0 m^n = 2^0 = 1 In this first example, let’s choose a binary function that ignores both its parameters. This is to stress the fact that the property holds regardless of the particular values contained in the lists. assert( List(1, 2, 3).foldM(9)((_, _) => List(0, 0)) == List(0, 0, 0, 0, 0, 0, 0, 0) ) m=2 z n=3 m^n = 2^3 = 8 Let f = (_, _) => List(0, 0) List(x1,x2,x3) = List(1,2,3) a1 = 9 In List(x1, x2, ..., xm).foldM(a1)(f) i.e. (for a2 <- f(a1,x1) a3 <- f(a2,x2) ... yield f(am,xm)).flatten Result: List(0,0,0,0,0,0,0,0) 301

Slide 302

Slide 302 text

Invocation m n Result Result length (m^n) foldM (\_ -> \_ -> [0,0]) 9 [] 2 0 [9] 2^0=1 foldM (\_ -> \_ -> [0,0]) 9 [1] 2 1 [0,0] 2^1=2 302 \_ -> \_ -> [0,0] x => y => List(0,0)

Slide 303

Slide 303 text

[0,0] [0,0] 303 Invocation m n Result Result length (m^n) foldM (\_ -> \_ -> [0,0]) 9 [] 2 0 [9] 2^0=1 foldM (\_ -> \_ -> [0,0]) 9 [1] 2 1 [0,0] 2^1=2

Slide 304

Slide 304 text

[0,0] [0,0] [0,0] Invocation m n Result Result length (m^n) foldM (\_ -> \_ -> [0,0]) 9 [] 2 0 [9] 2^0=1 foldM (\_ -> \_ -> [0,0]) 9 [1] 2 1 [0,0] 2^1=2 foldM (\_ -> \_ -> [0,0]) 9 [1,2] 2 2 [0,0,0,0] 2^2=4 [0,0,0,0] [0,0] 304

Slide 305

Slide 305 text

[0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] Invocation m n Result Result length (m^n) foldM (\_ -> \_ -> [0,0]) 9 [] 2 0 [9] 2^0=1 foldM (\_ -> \_ -> [0,0]) 9 [1] 2 1 [0,0] 2^1=2 foldM (\_ -> \_ -> [0,0]) 9 [1,2] 2 2 [0,0,0,0] 2^2=4 foldM (\_ -> \_ -> [0,0]) 9 [1,2,3] 2 3 [0,0,0,0,0,0,0,0] 2^3=8 [0,0,0,0,0,0,0,0] [0,0,0,0] [0,0] 305

Slide 306

Slide 306 text

[0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] [0,0] Invocation m n Result Result length (m^n) foldM (\_ -> \_ -> [0,0]) 9 [] 2 0 [9] 2^0=1 foldM (\_ -> \_ -> [0,0]) 9 [1] 2 1 [0,0] 2^1=2 foldM (\_ -> \_ -> [0,0]) 9 [1,2] 2 2 [0,0,0,0] 2^2=4 foldM (\_ -> \_ -> [0,0]) 9 [1,2,3] 2 3 [0,0,0,0,0,0,0,0] 2^3=8 foldM (\_ -> \_ -> [0,0]) 9 [1,2,3,4] 2 4 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 2^4=16 [0,0,0,0,0,0,0,0] [0,0,0,0] [0,0] [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 306

Slide 307

Slide 307 text

In this second example, we change the binary function that we pass to foldM so that… 307

Slide 308

Slide 308 text

In this second example, we change the binary function that we pass to foldM so that… …rather than ignoring its two parameters (the accumulator acc and the current list element x) and always returning the same two-element list List(0,0) … (_, _) => List(0, 0) 308

Slide 309

Slide 309 text

In this second example, we change the binary function that we pass to foldM so that… …rather than ignoring its two parameters (the accumulator acc and the current list element x) and always returning the same two-element list List(0,0) … (_, _) => List(0, 0) …the function now returns a two-element list containing 1. the result of adding the current element to the accumulator and 2. the result of subtracting the current element from the accumulator (acc,x) => List(acc+x, acc-x) 309

Slide 310

Slide 310 text

Let f = (acc,x) => List(acc+x, acc-x) List(x1,x2,x3) = List(1,2,3) a1 = 0 In List(x1, x2, ..., xm).foldM(a1)(f) i.e. (for a2 <- f(a1,x1) a3 <- f(a2,x2) ... yield f(am,xm)).flatten Result: List(6,0,2,-4,4,-2,0,-6)) assert( List(1,2,3).foldM(0)((acc,x) => List(acc+x, acc-x)) == List(6,0,2,-4,4,-2,0,-6) ) 310

Slide 311

Slide 311 text

Invocation Result Result length (m^n) foldM (\acc x -> [acc + x, acc - x]) 0 [] [0] 2^0=1 0 [0] 311

Slide 312

Slide 312 text

Invocation Result Result length (m^n) foldM (\acc x -> [acc + x, acc - x]) 0 [] [0] 2^0=1 foldM (\acc x -> [acc + x, acc - x]) 0 [1] [1,-1] 2^1=2 0 1 -1 +1 -1 [1,-1] [0] 312

Slide 313

Slide 313 text

Invocation Result Result length (m^n) foldM (\acc x -> [acc + x, acc - x]) 0 [] [0] 2^0=1 foldM (\acc x -> [acc + x, acc - x]) 0 [1] [1,-1] 2^1=2 foldM (\acc x -> [acc + x, acc - x]) 0 [1,2] [3,-1,1,-3] 2^2=4 0 1 -1 +1 -1 -1 3 -3 1 +2 -2 +2 -2 [3,-1,1,-3] [1,-1] [0] 313

Slide 314

Slide 314 text

Invocation Result Result length (m^n) foldM (\acc x -> [acc + x, acc - x]) 0 [] [0] 2^0=1 foldM (\acc x -> [acc + x, acc - x]) 0 [1] [1,-1] 2^1=2 foldM (\acc x -> [acc + x, acc - x]) 0 [1,2] [3,-1,1,-3] 2^2=4 foldM (\acc x -> [acc + x, acc - x]) 0 [1,2,3] [6,0,2,-4,4,-2,0,-6] 2^3=8 0 1 -1 +1 -1 -1 3 -3 1 0 6 -4 2 -2 4 -6 0 +2 -2 +2 -2 +3 -3 +3 -3 +3 -3 +3 -3 [6,0,2,-4,4,-2,0,-6] [3,-1,1,-3] [1,-1] [0] 314

Slide 315

Slide 315 text

In this third example, we change the binary function that we pass to foldM so that… 315

Slide 316

Slide 316 text

In this third example, we change the binary function that we pass to foldM so that… …rather than returning a two-element list containing 1) the result of adding the current element x to the accumulator 2) the result of subtracting x from the accumulator (acc,x) => List(acc+x, acc-x) 316

Slide 317

Slide 317 text

In this third example, we change the binary function that we pass to foldM so that… …rather than returning a two-element list containing 1) the result of adding the current element x to the accumulator 2) the result of subtracting x from the accumulator (acc,x) => List(acc+x, acc-x) …it returns a two element list containing 1) the result of adding x to the front of the accumulator list 2) the accumulator list (acc,x) => List(x::acc, acc) As a result, foldM computes the powerset of its list parameter. 317

Slide 318

Slide 318 text

Let f = (acc,x) => List(x::acc, acc) List(x1,x2,x3) = List(1,2,3) a1 = List.empty In List(x1, x2, ..., xm).foldM(a1)(f) i.e. (for a2 <- f(a1,x1) a3 <- f(a2,x2) ... yield f(am,xm)).flatten Result: List( List(3,2,1), List(2,1), List(3,1), List(1), List(3,2), List(2), List(3), List())) assert( List(1,2,3).foldM(List.empty)((acc,x) => List(x::acc, acc)) == List( List(3,2,1), List(2,1), List(3,1), List(1), List(3,2), List(2), List(3), List()) ) ) 318

Slide 319

Slide 319 text

Invocation Result Result length (m^n) foldM (\acc x -> [x:acc,acc]) [] [] [[]] 2^0=1 [] [[]] 319

Slide 320

Slide 320 text

Invocation Result Result length (m^n) foldM (\acc x -> [x:acc,acc]) [] [] [[]] 2^0=1 foldM (\acc x -> [x:acc,acc]) [] [1] [[1],[]] 2^1=2 [] [1] [] 1: [[]] [[1],[]] 320

Slide 321

Slide 321 text

Invocation Result Result length (m^n) foldM (\acc x -> [x:acc,acc]) [] [] [[]] 2^0=1 foldM (\acc x -> [x:acc,acc]) [] [1] [[1],[]] 2^1=2 foldM (\acc x -> [x:acc,acc]) [] [1,2] [[2,1],[1],[2],[]] 2^2=4 [] [1] [] [2,1] [1] [2] [] 2: 2: 1: [[]] [[1],[]] [[2,1],[1],[2],[]] 321

Slide 322

Slide 322 text

Invocation Result Result length (m^n) foldM (\acc x -> [x:acc,acc]) [] [] [[]] 2^0=1 foldM (\acc x -> [x:acc,acc]) [] [1] [[1],[]] 2^1=2 foldM (\acc x -> [x:acc,acc]) [] [1,2] [[2,1],[1],[2],[]] 2^2=4 foldM (\acc x -> [x:acc,acc]) [] [1,2,3] [[3,2,1],[2,1],[3,1],[1],[3,2],[2],[3],[]] 2^3=8 [] [1] [] [2,1] [1] [2] [] [3,2,1] [2,1] [3,1] [1] [3,2] [2] [3] [] 3: 3: 3: 3: 2: 2: 1: [[]] [[1],[]] [[2,1],[1],[2],[]] [[3,2,1],[2,1],[3,1],[1],[3,2],[2],[3],[]] 322

Slide 323

Slide 323 text

Invocation Result Result length (m^n) foldM (\acc x -> [x:acc,acc]) [] [] [[]] 2^0=1 foldM (\acc x -> [x:acc,acc]) [] [1] [[1],[]] 2^1=2 foldM (\acc x -> [x:acc,acc]) [] [1,2] [[2,1],[1],[2],[]] 2^2=4 foldM (\acc x -> [x:acc,acc]) [] [1,2,3] [[3,2,1],[2,1],[3,1],[1],[3,2],[2],[3],[]] 2^3=8 [] [1] [] [2,1] [1] [2] [] [3,2,1] [2,1] [3,1] [1] [3,2] [2] [3] [] 3: 3: 3: 3: 2: 2: 1: [[]] [[1],[]] [[2,1],[1],[2],[]] [[3,2,1],[2,1],[3,1],[1],[3,2],[2],[3],[]] Yes, this is the same process that we saw in this previous example: List(1, 2, 3).filterM(_ => List(true, false)) So the following also computes the powerset of the input list: List(1, 2, 3).foldM(List.empty)((acc,x) => List(x::acc, acc)) 323

Slide 324

Slide 324 text

assert( List(1,2,3,4).foldM(9)((_,_) => List(0, 0)) == List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)) assert( List(1,2,3).foldM(0)((acc,x) => List(acc+x, acc-x)) == List(6,0,2,-4,4,-2,0,-6)) assert( List(1,2,3).foldM(List.empty)((acc,x) => List(x::acc,acc)) == List(List(3,2,1),List(2,1),List(3,1),List(1),List(3,2),List(2),List(3),List())) assertEqual "foldM test 1" (foldM (\_ _ -> [0,0]) 9 [1,2,3,4]) [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] assertEqual "foldM test 2" (foldM (\acc x -> [acc+x,acc-x]) 0 [1,2,3]) [6,0,2,-4,4,-2,0,-6] assertEqual "foldM test 3" (foldM (\acc x -> [x:acc,acc])[] [1,2,3]) [[3,2,1],[2,1],[3,1],[1],[3,2],[2],[3],[]] import cats.syntax.foldable._ import Control.Monad Cats The three foldM examples that we have just gone through In the rest of this talk we’ll refer to the function passed to foldM as an updater function 324

mapM λ 325

mapM λ 326

Slide 327

Slide 327 text

Now that we have gained some familiarity with the foldM function... …let’s begin to see how it can be used to solve the N-Queens combinatorial problem. 327

Slide 328

Slide 328 text

Let’s refer to the solution that uses foldM as the folding queens solution. The folding queens solution needs to generate the permutations of a list of integers. 328

Slide 329

Slide 329 text

N = 4 # of permutations = 44 = 256 Permutations with repetition allowed 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 329

Slide 330

Slide 330 text

1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 Permutations 1 2 3 List The number of permutations of a list of length n is n!, the factorial of n. e.g. the number of permutations of a list of three elements is 3! = 3 * 2 * 1 = 6 Permutations with repetition not allowed 330

Slide 331

Slide 331 text

1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 Permutations 1 2 3 List 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 Permutations 1 2 3 List The number of permutations of a list of length n is n!, the factorial of n. e.g. the number of permutations of a list of three elements is 3! = 3 * 2 * 1 = 6 Permutations with repetition not allowed 331

Slide 332

Slide 332 text

Permutations List 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 Permutations 1 2 3 List 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 Permutations 1 2 3 List The number of permutations of a list of length n is n!, the factorial of n. e.g. the number of permutations of a list of three elements is 3! = 3 * 2 * 1 = 6 Permutations with repetition not allowed 332

Slide 333

Slide 333 text

update :: Eq a => ([a], [a]) -> p -> [([a], [a])] update (permutation,choices) _ = [(choice:permutation, delete choice choices) | choice <- choices] Let’s look at an updater function that can be used to generate the permutations of a list. 333

Slide 334

Slide 334 text

update :: Eq a => ([a], [a]) -> p -> [([a], [a])] update (permutation,choices) _ = [(choice:permutation, delete choice choices) | choice <- choices] Invocation foldM update ([],[1,2,3]) [1,2,3] Result [([3,2,1],[]), ([2,3,1],[]), ([3,1,2],[]), ([1,3,2],[]), ([2,1,3],[]), ([1,2,3],[])] Let’s look at an updater function that can be used to generate the permutations of a list. 334

Slide 335

Slide 335 text

[] [1,2,3] ( , ) Invocation Result foldM update ([],[1,2,3]) [] [([],[1,2,3])] 335

Slide 336

Slide 336 text

[1] [2,3] [3] [1,2] ( , ) ( , ) [] [1,2,3] ( , ) [2] [1,3] ( , ) Invocation Result foldM update ([],[1,2,3]) [] [([],[1,2,3])] foldM update ([],[1,2,3]) [1] [([1],[2,3]), ([2],[1,3]), ([3],[1,2])] choose 1 choose 2 choose 3 336

Slide 337

Slide 337 text

( , ) ( , ) ( , ) ( , ) ( , ) ( , ) [2,3] [1,3] [3,2] [1,2] [3,1] [2,1] [3] [1] [2,3] [3] [1,2] ( , ) ( , ) [] [1,2,3] ( , ) [2] [1,3] ( , ) [3] [1] [2] [1] [2] Invocation Result foldM update ([],[1,2,3]) [] [([],[1,2,3])] foldM update ([],[1,2,3]) [1] [([1],[2,3]), ([2],[1,3]), ([3],[1,2])] foldM update ([],[1,2,3]) [1,2] [([2,1],[3]), ([3,1],[2]), ([1,2],[3]), ([3,2],[1]), ([1,3],[2]), ([2,3],[1])] choose 1 choose 2 choose 3 choose 2 choose 3 choose 1 choose 3 choose 1 choose 2 337

Slide 338

Slide 338 text

( , ) ( , ) ( , ) ( , ) ( , ) ( , ) ( , ) ( , ) ( , ) ( , ) ( , ) ( , ) [1,2,3] [2,1,3] [1,3,2] [3,1,2] [2,3,1] [2,3] [1,3] [3,2] [1,2] [3,1] [2,1] [3] [1] [2,3] [3] [1,2] ( , ) ( , ) [] [1,2,3] ( , ) [2] [1,3] ( , ) [3] [1] [2] [1] [2] [3,2,1] [] [] [] [] [] [] Invocation Result foldM update ([],[1,2,3]) [] [([],[1,2,3])] foldM update ([],[1,2,3]) [1] [([1],[2,3]), ([2],[1,3]), ([3],[1,2])] foldM update ([],[1,2,3]) [1,2] [([2,1],[3]), ([3,1],[2]), ([1,2],[3]), ([3,2],[1]), ([1,3],[2]), ([2,3],[1])] foldM update ([],[1,2,3]) [1,2,3] [([3,2,1],[]), ([2,3,1],[]), ([3,1,2],[]), ([1,3,2],[]), ([2,1,3],[]), ([1,2,3],[])] choose 1 choose 2 choose 3 choose 2 choose 3 choose 1 choose 3 choose 1 choose 2 choose 3 choose 2 choose 3 choose 1 choose 2 choose 1 338 Size of input list being folded: N Size of output list containing permutations: N!

Slide 339

Slide 339 text

The result of foldM is not exactly a list of permutations, but rather, a list of a pair of a permutation and the empty list. 339

Slide 340

Slide 340 text

permute :: Eq a => [a] -> [([a], [a])] permute xs = foldM update ([],xs) xs permutations :: (Eq a) => [a] -> [[a]] permutations xs = map fst (permute xs) > permutations [] [[]] > permutations [1] [[1]] > permutations [1,2] [[2,1],[1,2]] > permutations [1,2,3] [[3,2,1],[2,3,1],[3,1,2],[1,3,2],[2,1,3],[1,2,3]] > permutations [1,2,3,4] [[4,3,2,1],[3,4,2,1],[4,2,3,1],[2,4,3,1],[3,2,4,1] ,[2,3,4,1],[4,3,1,2],[3,4,1,2],[4,1,3,2],[1,4,3,2] ,[3,1,4,2],[1,3,4,2],[4,2,1,3],[2,4,1,3],[4,1,2,3] ,[1,4,2,3],[2,1,4,3],[1,2,4,3],[3,2,1,4],[2,3,1,4] ,[3,1,2,4],[1,3,2,4],[2,1,3,4],[1,2,3,4]] The result of foldM is not exactly a list of permutations, but rather, a list of a pair of a permutation and the empty list. Let’s defined a couple of helper functions to make it more convenient to get hold of the desired list of permutations. 340

Slide 341

Slide 341 text

def update[A](acc:(List[A], List[A]), a:A): List[(List[A], List[A])] = acc match case (permutation, choices) => for choice <- choices yield (choice :: permutation, choices.diff(List(choice))) assert( List(1,2,3).foldM((List.empty[Int],List(1,2,3)))(update) == List( (List(3,2,1),List()), (List(2,3,1),List()), (List(3,1,2),List()), (List(1,3,2),List()), (List(2,1,3),List()), (List(1,2,3),List())) ) Cats 341

Slide 342

Slide 342 text

def permute[A](as: List[A]): List[(List[A], List[A])] = as.foldM((List.empty[A], as))(update) def permutations[A](as: List[A]): List[List[A]] = permute(as).map(_.head) Cats assert( permutations(List(1,2,3)) == List( List(3, 2, 1), List(2, 3, 1), List(3, 1, 2), List(1, 3, 2), List(2, 1, 3), List(1, 2, 3) ) ) 342

Slide 343

Slide 343 text

update (permutation,choices) _ = [(choice:permutation, delete choice choices) | choice <- choices] oneMoreQueen (queens,emptyColumns) _ = [(queen:queens, delete queen emptyColumns) | queen <- emptyColumns] We want to use foldM to solve the N-Queens combinatorial problem. So let’s rename the variables of the update function to reflect the fact that the permutations that we want to generate are the possible lists of positions (columns) of 𝑛 queens on an 𝑛×𝑛 board. def oneMoreQueen[A](acc:(List[A], List[A]), a:A): List[(List[A], List[A])] = acc match case (queens, emptyColumns) => for queen <- emptyColumns yield (queen :: queens, emptyColumns.diff(List(queen))) def update[A](acc:(List[A], List[A]), a:A): List[(List[A], List[A])] = acc match case (permutation, choices) => for choice <- choices yield (choice :: permutation, choices.diff(List(choice))) 343

Slide 344

Slide 344 text

Not all permutations are valid though: we need to filter out unsafe permutations. Just like in the recursive solution, we are going to determine if a permutation is safe is by using a safe function. Let’s add to oneMoreQueen a filter that invokes the safe function. oneMoreQueen (queens, emptyColumns) _ = [(queen:queens, delete queen emptyColumns) | queen <- emptyColumns] oneMoreQueen (queens, emptyColumns) _ = [(queen:queens, delete queen emptyColumns) | queen <- emptyColumns,isSafe queen] def oneMoreQueen[A](acc:(List[A], List[A]), a:A): List[(List[A], List[A])] = acc match case (queens, emptyColumns) => for queen <- emptyColumns yield (queen :: queens, emptyColumns.diff(List(queen))) def oneMoreQueen[A](acc:(List[A], List[A]), a:A): List[(List[A], List[A])] = acc match case (queens, emptyColumns) => for queen <- emptyColumns if isSafe(queen) yield (queen :: queens, emptyColumns.diff(List(queen))) 344

Slide 345

Slide 345 text

Earlier we tried out our update function as follows (to compute permutations) foldM update ([],[1,2,3]) [1,2,3] List(1,2,3).foldM((List.empty[Int],List(1,2,3)))(update) 345

Slide 346

Slide 346 text

Earlier we tried out our update function as follows (to compute permutations) foldM update ([],[1,2,3]) [1,2,3] List(1,2,3).foldM((List.empty[Int],List(1,2,3)))(update) The queens function that we need to implement is this: queens :: Int -> [[Int]] queens n = ??? def queens(n: Int): List[List[Int]] = ??? 346 queens(4) List(List(3,1,4,2),List(2,4,1,3)))

Slide 347

Slide 347 text

Earlier we tried out our update function as follows (to compute permutations) foldM update ([],[1,2,3]) [1,2,3] List(1,2,3).foldM((List.empty[Int],List(1,2,3)))(update) The queens function that we need to implement is this: queens :: Int -> [[Int]] queens n = ??? def queens(n: Int): List[List[Int]] = ??? Given that we have renamed update to oneMoreQueen, here is how we need to call foldM: foldM oneMoreQueen ([],[1..n]) [1..n] List(1 to n *).foldM(List.empty[Int], List(1 to n *))(oneMoreQueen) 347 queens(4) List(List(3,1,4,2),List(2,4,1,3))) update oneMoreQueen 1..3 1..n

Slide 348

Slide 348 text

Earlier we tried out our update function as follows (to compute permutations) foldM update ([],[1,2,3]) [1,2,3] List(1,2,3).foldM((List.empty[Int],List(1,2,3)))(update) The queens function that we need to implement is this: queens :: Int -> [[Int]] queens n = ??? def queens(n: Int): List[List[Int]] = ??? Given that we have renamed update to oneMoreQueen, here is how we need to call foldM: foldM oneMoreQueen ([],[1..n]) [1..n] List(1 to n *).foldM(Nil, List(1 to n *))(oneMoreQueen) We saw earlier that in order to extract the list of permutations / queens from the result of update / oneMoreQueen, we need to map over the result list a function that takes the first element of each pair in the list. So here is how we implement queens: queens :: Int -> [[Int]] queens n = map fst (foldM oneMoreQueen ([],[1..n]) [1..n]) def queens(n: Int): List[List[Int]] = List(1 to n *).foldM(Nil, List(1 to n *))(oneMoreQueen).map(_.head) 348 queens(4) List(List(3,1,4,2),List(2,4,1,3))) update oneMoreQueen 1..3 1..n List[(List[Int], List[Int])] List[List[Int]]

Slide 349

Slide 349 text

def queens(n: Int): List[List[Int]] = def oneMoreQueen(acc:(List[Int],List[Int]),x:Int): List[(List[Int],List[Int])] = acc match { case (queens, emptyColumns) => def isSafe(x:Int): Boolean = { for (c,n) <- queens zip (1 to n) yield x != c + n && x != c – n } forall identity for queen <- emptyColumns if isSafe(queen) yield (queen::queens, emptyColumns diff List(queen)) } val oneToN = List(1 to n *) oneToN.foldM(Nil, oneToN)(oneMoreQueen).map(_.head) queens :: Int -> [[Int]] queens n = map fst (foldM oneMoreQueen ([],[1..n]) [1..n]) where oneMoreQueen (queens, emptyColumns) _ = [(queen:queens, delete queen emptyColumns) | queen <- emptyColumns, isSafe queen] where isSafe x = and [x /= c + n && x /= c - n | (n,c) <- zip [1..] queens] import cats.syntax.foldable._ Cats import Control.Monad 349

Slide 350

Slide 350 text

Iterative Algorithm Recursive Algorithm 350

Slide 351

Slide 351 text

queens n = placeQueens n where placeQueens 0 = [[]] placeQueens k = [queen:queens | queens <- placeQueens(k-1), queen <- [1..n], isSafe queen queens] isSafe 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 isSafe(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 isSafe(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 351

Slide 352

Slide 352 text

queens n = placeQueens n where placeQueens 0 = [[]] placeQueens k = [queen:queens | queens <- placeQueens(k-1), queen <- [1..n], isSafe queen queens] queens :: Int -> [[Int]] queens n = map fst (foldM oneMoreQueen ([],[1..n]) [1..n]) where oneMoreQueen (queens,emptyColumns) _ = [(queen:queens, delete queen emptyColumns) | queen <- emptyColumns, isSafe queen] where isSafe x = and [x /= c + n && x /= c - n | (n,c) <- zip [1..] queens] Recursive Algorithm Iterative Algorithm 352

Slide 353

Slide 353 text

queens :: Int -> [[Int]] queens n = map fst (foldM oneMoreQueen ([],[1..n]) [1..n]) where oneMoreQueen (queens, emptyColumns) _ = [(queen:queens, delete queen emptyColumns) | queen <- emptyColumns, isSafe queen] where isSafe x = and [x /= c + n && x /= c - n | (n,c) <- zip [1..] queens] -- given n, "queens n" solves the n-queens problem, returning a list of all the -- safe arrangements. each solution is a list of the columns where the queens are -- located for each row queens :: Int -> [[Int]] queens n = map fst \$ foldM oneMoreQueen ([],[1..n]) [1..n] where -- foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a -- foldM folds (from left to right) in the list monad, which is convenient for -- "nondeterminstically" finding "all possible solutions" of something. the -- initial value [] corresponds to the only safe arrangement of queens in 0 rows -- given a safe arrangement y of queens in the first i rows, and a list of -- possible choices, "oneMoreQueen y _" returns a list of all the safe -- arrangements of queens in the first (i+1) rows along with remaining choices oneMoreQueen (y,d) _ = [(x:y, delete x d) | x <- d, safe x] where -- "safe x" tests whether a queen at column x is safe from previous queens safe x = and [x /= c + n && x /= c - n | (n,c) <- zip [1..] y] https://rosettacode.org/wiki/N-queens_problem#Haskell Rosetta Code Version Our Version 353

Slide 354

Slide 354 text

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) Recursive Algorithm def queens(n: Int): List[List[Int]] = def oneMoreQueen(acc: (List[Int],List[Int]), x: Int): List[(List[Int],List[Int])] = acc match case (queens, emptyColumns) => def isSafe(queen:Int): Boolean = … for queen <- emptyColumns if isSafe(queen) yield (queen::queens, emptyColumns diff List(queen)) List(1 to n *).foldM(Nil, List(1 to n *))(oneMoreQueen).map(_.head) import cats.syntax.foldable._ Cats Iterative Algorithm 354

Slide 355

Slide 355 text

N = 8 92 solutions 355

Slide 356

Slide 356 text

> queens 8 [[4,2,7,3,6,8,5,1],[5,2,4,7,3,8,6,1],[3,5,2,8,6,4,7,1],[3,6,4,2,8,5,7,1],[5,7,1,3,8,6,4,2] ,[4,6,8,3,1,7,5,2],[3,6,8,1,4,7,5,2],[5,3,8,4,7,1,6,2],[5,7,4,1,3,8,6,2],[4,1,5,8,6,3,7,2] ,[3,6,4,1,8,5,7,2],[4,7,5,3,1,6,8,2],[6,4,2,8,5,7,1,3],[6,4,7,1,8,2,5,3],[1,7,4,6,8,2,5,3] ,[6,8,2,4,1,7,5,3],[6,2,7,1,4,8,5,3],[4,7,1,8,5,2,6,3],[5,8,4,1,7,2,6,3],[4,8,1,5,7,2,6,3] ,[2,7,5,8,1,4,6,3],[1,7,5,8,2,4,6,3],[2,5,7,4,1,8,6,3],[4,2,7,5,1,8,6,3],[5,7,1,4,2,8,6,3] ,[6,4,1,5,8,2,7,3],[5,1,4,6,8,2,7,3],[5,2,6,1,7,4,8,3],[6,3,7,2,8,5,1,4],[2,7,3,6,8,5,1,4] ,[7,3,1,6,8,5,2,4],[5,1,8,6,3,7,2,4],[1,5,8,6,3,7,2,4],[3,6,8,1,5,7,2,4],[6,3,1,7,5,8,2,4] ,[7,5,3,1,6,8,2,4],[7,3,8,2,5,1,6,4],[5,3,1,7,2,8,6,4],[2,5,7,1,3,8,6,4],[3,6,2,5,8,1,7,4] ,[6,1,5,2,8,3,7,4],[8,3,1,6,2,5,7,4],[2,8,6,1,3,5,7,4],[5,7,2,6,3,1,8,4],[3,6,2,7,5,1,8,4] ,[6,2,7,1,3,5,8,4],[3,7,2,8,6,4,1,5],[6,3,7,2,4,8,1,5],[4,2,7,3,6,8,1,5],[7,1,3,8,6,4,2,5] ,[1,6,8,3,7,4,2,5],[3,8,4,7,1,6,2,5],[6,3,7,4,1,8,2,5],[7,4,2,8,6,1,3,5],[4,6,8,2,7,1,3,5] ,[2,6,1,7,4,8,3,5],[2,4,6,8,3,1,7,5],[3,6,8,2,4,1,7,5],[6,3,1,8,4,2,7,5],[8,4,1,3,6,2,7,5] ,[4,8,1,3,6,2,7,5],[2,6,8,3,1,4,7,5],[7,2,6,3,1,4,8,5],[3,6,2,7,1,4,8,5],[4,7,3,8,2,5,1,6] ,[4,8,5,3,1,7,2,6],[3,5,8,4,1,7,2,6],[4,2,8,5,7,1,3,6],[5,7,2,4,8,1,3,6],[7,4,2,5,8,1,3,6] ,[8,2,4,1,7,5,3,6],[7,2,4,1,8,5,3,6],[5,1,8,4,2,7,3,6],[4,1,5,8,2,7,3,6],[5,2,8,1,4,7,3,6] ,[3,7,2,8,5,1,4,6],[3,1,7,5,8,2,4,6],[8,2,5,3,1,7,4,6],[3,5,2,8,1,7,4,6],[3,5,7,1,4,2,8,6] ,[5,2,4,6,8,3,1,7],[6,3,5,8,1,4,2,7],[5,8,4,1,3,6,2,7],[4,2,5,8,6,1,3,7],[4,6,1,5,2,8,3,7] ,[6,3,1,8,5,2,4,7],[5,3,1,6,8,2,4,7],[4,2,8,6,1,3,5,7],[6,3,5,7,1,4,2,8],[6,4,7,1,3,5,2,8] ,[4,7,5,2,6,1,3,8],[5,7,2,6,3,1,4,8]] > length (queens 8) 92 356

Slide 357

Slide 357 text

assert( queens(8) == List( List(4,2,7,3,6,8,5,1),List(5,2,4,7,3,8,6,1),List(3,5,2,8,6,4,7,1),List(3,6,4,2,8,5,7,1),List(5,7,1,3,8,6,4,2), List(4,6,8,3,1,7,5,2),List(3,6,8,1,4,7,5,2),List(5,3,8,4,7,1,6,2),List(5,7,4,1,3,8,6,2),List(4,1,5,8,6,3,7,2), List(3,6,4,1,8,5,7,2),List(4,7,5,3,1,6,8,2),List(6,4,2,8,5,7,1,3),List(6,4,7,1,8,2,5,3),List(1,7,4,6,8,2,5,3), List(6,8,2,4,1,7,5,3),List(6,2,7,1,4,8,5,3),List(4,7,1,8,5,2,6,3),List(5,8,4,1,7,2,6,3),List(4,8,1,5,7,2,6,3), List(2,7,5,8,1,4,6,3),List(1,7,5,8,2,4,6,3),List(2,5,7,4,1,8,6,3),List(4,2,7,5,1,8,6,3),List(5,7,1,4,2,8,6,3), List(6,4,1,5,8,2,7,3),List(5,1,4,6,8,2,7,3),List(5,2,6,1,7,4,8,3),List(6,3,7,2,8,5,1,4),List(2,7,3,6,8,5,1,4), List(7,3,1,6,8,5,2,4),List(5,1,8,6,3,7,2,4),List(1,5,8,6,3,7,2,4),List(3,6,8,1,5,7,2,4),List(6,3,1,7,5,8,2,4), List(7,5,3,1,6,8,2,4),List(7,3,8,2,5,1,6,4),List(5,3,1,7,2,8,6,4),List(2,5,7,1,3,8,6,4),List(3,6,2,5,8,1,7,4), List(6,1,5,2,8,3,7,4),List(8,3,1,6,2,5,7,4),List(2,8,6,1,3,5,7,4),List(5,7,2,6,3,1,8,4),List(3,6,2,7,5,1,8,4), List(6,2,7,1,3,5,8,4),List(3,7,2,8,6,4,1,5),List(6,3,7,2,4,8,1,5),List(4,2,7,3,6,8,1,5),List(7,1,3,8,6,4,2,5), List(1,6,8,3,7,4,2,5),List(3,8,4,7,1,6,2,5),List(6,3,7,4,1,8,2,5),List(7,4,2,8,6,1,3,5),List(4,6,8,2,7,1,3,5), List(2,6,1,7,4,8,3,5),List(2,4,6,8,3,1,7,5),List(3,6,8,2,4,1,7,5),List(6,3,1,8,4,2,7,5),List(8,4,1,3,6,2,7,5), List(4,8,1,3,6,2,7,5),List(2,6,8,3,1,4,7,5),List(7,2,6,3,1,4,8,5),List(3,6,2,7,1,4,8,5),List(4,7,3,8,2,5,1,6), List(4,8,5,3,1,7,2,6),List(3,5,8,4,1,7,2,6),List(4,2,8,5,7,1,3,6),List(5,7,2,4,8,1,3,6),List(7,4,2,5,8,1,3,6), List(8,2,4,1,7,5,3,6),List(7,2,4,1,8,5,3,6),List(5,1,8,4,2,7,3,6),List(4,1,5,8,2,7,3,6),List(5,2,8,1,4,7,3,6), List(3,7,2,8,5,1,4,6),List(3,1,7,5,8,2,4,6),List(8,2,5,3,1,7,4,6),List(3,5,2,8,1,7,4,6),List(3,5,7,1,4,2,8,6), List(5,2,4,6,8,3,1,7),List(6,3,5,8,1,4,2,7),List(5,8,4,1,3,6,2,7),List(4,2,5,8,6,1,3,7),List(4,6,1,5,2,8,3,7), List(6,3,1,8,5,2,4,7),List(5,3,1,6,8,2,4,7),List(4,2,8,6,1,3,5,7),List(6,3,5,7,1,4,2,8),List(6,4,7,1,3,5,2,8), List(4,7,5,2,6,1,3,8),List(5,7,2,6,3,1,4,8)) ) assert(queens(8).size == 92) 357

Slide 358

Slide 358 text

The recursive algorithm has no ‘memory’ of the columns in which queens have already been placed, so it will generate and test a permutation for safety, even if it contains repetitions, like the following one. The iterative algorithm does have ‘memory’, in that permutations with repetition, like the one above, are not even considered as candidate solutions, i.e. it ‘remembers’ where it has already placed previous queens. 358

Slide 359

Slide 359 text

Without the isSafe function, the recursive algorithm would generate 44 = 256 candidate solution boards. 359

Slide 360

Slide 360 text

With the isSafe function, the recursive algorithm generates 60 boards. 360

Slide 361

Slide 361 text

recursive solution (no memory) 361

Slide 362

Slide 362 text

recursive solution (no memory) 362

Slide 363

Slide 363 text

recursive solution (no memory) 363

Slide 364

Slide 364 text

recursive solution (no memory) 364

Slide 365

Slide 365 text

recursive solution (no memory) Boards generated & ested (in red): 60 (out of 256) 365

Slide 366

Slide 366 text

Of the 60 boards generated, 16 are candidate solution boards. 366

Slide 367

Slide 367 text

recursive solution (no memory) Boards generated & ested (in red): 60 (out of 256) 367

Slide 368

Slide 368 text

recursive solution (no memory) Candidate boards enerated & tested in pink): 16 368

Slide 369

Slide 369 text

Lets compare that with the iterative solution 369

Slide 370

Slide 370 text

Without the isSafe function, the iterative algorithm would generate 4! = 24 candidate solution boards. 370

Slide 371

Slide 371 text

With the isSafe function, the iterative algorithm generates 32 boards. 371

Slide 372

Slide 372 text

iterative solution (has memory) 372

Slide 373

Slide 373 text

iterative solution (has memory) 373

Slide 374

Slide 374 text

iterative solution (has memory) 374

Slide 375

Slide 375 text

iterative solution (has memory) 375

Slide 376

Slide 376 text

iterative solution (has memory) 376 Boards generated & ested (in red): 32 (out of 256)

Slide 377

Slide 377 text

Of the 32 boards generated, 4 are candidate solution boards. 377

Slide 378

Slide 378 text

iterative solution (has memory) Boards generated & ested (in red): 32 (out of 256) 378

Slide 379

Slide 379 text

iterative solution (has memory) Candidate boards generated & tested in pink): 4 379

Slide 380

Slide 380 text

Algorithm without isSafe function with isSafe function Boards Generated (All of them candidate solutions) Boards Generated Candidate Boards Generated Recursive 256 (44) 60 16 Iterative 24 (4!) 32 4 N=4; 2 solutions 380

Slide 381

Slide 381 text

381 Algorithm without isSafe function with isSafe function Boards Generated (All of them candidate solutions) Boards Generated Candidate Boards Generated Recursive 3,125 (55) 220 60 Iterative 120 (5!) 101 12 N=5; 10 solutions Algorithm without isSafe function with isSafe function Boards Generated (All of them candidate solutions) Boards Generated Candidate Boards Generated Recursive 46,656 (66) 893 240 Iterative 720 (6!) 356 40 N=6; 4 solutions

Slide 382

Slide 382 text

382 Algorithm without isSafe function with isSafe function Boards Generated (All of them candidate solutions) Boards Generated Candidate Boards Generated Recursive 823,543 (77) 3,581 657 Iterative 5,040 (7!) 1,344 93 N=7; 40 solutions Algorithm without isSafe function with isSafe function Boards Generated (All of them candidate solutions) Boards Generated Candidate Boards Generated Recursive 16,777,216 (88) 15,718 2,495 Iterative 40,320 (8!) 5,506 312 N=8; 92 solutions

Slide 383

Slide 383 text

Without using isSafe function 𝑁! 𝑁𝑁 383

Slide 384

Slide 384 text

Using isSafe function 384

Slide 385

Slide 385 text

Using isSafe function 385

386