Filippo Vitale
July 08, 2015
400

Send + More = Money – ScalaSyd July 2015

Let’s mash 2 monads to solve a simple CSP

Intermediate–level Scala talk that cover the 4 blog posts by Bartosz Milewski on solving a simple constraint satisfaction problem in C++ and Haskell.

Starting from an imperative–style implementation we will get to a pure FP–style solution surprisingly similar to the Haskell one promoted by Bartosz.

code: https://goo.gl/0BkWgW
video: https://goo.gl/u71jcH

July 08, 2015

Transcript

1. July 2015 @filippovitale Send + More = Money Let’s merge

2 monads to solve a simple “constraint satisfaction problem”
2. None

16. None

18. Mon*d

loops = loops : gotos - Declarative vs. Imperative - Reusable ⊛ Composable ⊛ Factorisable - Easily parallelisable
20. Brute force 10!/(10 – 8)! ≈ 2 Million How big

is the search space?

22. for { s <- 0 to 9 if s !=

0 e <- 0 to 9 if e != s n <- 0 to 9 if n != s && n != e d <- 0 to 9 if d != s && d != e && d!=n ... for (int s = 0; s < 10; ++s) for (int e = 0; e < 10; ++e) for (int n = 0; n < 10; ++n) for (int d = 0; d < 10; ++d) ... s != 0 e != s n != s && n != e d != s && d != e && d != n The solution boils down to: ➔ generating all those substitutions ➔ testing the constraints for each one
23. val listOfDigits = (0 to 9).toList val solutions = for

{ s <- listOfDigits if s != 0 e <- listOfDigits if s != e n <- listOfDigits if s != n && e != n d <- listOfDigits if s != d && e != d && n != d m <- listOfDigits if m != 0 if s != m && e != m && n != m && d != m o <- listOfDigits if s != o && e != o && n != o && d != o && m != o r <- listOfDigits if s != r && e != r && n != r && d != r && m != r && o != r y <- listOfDigits if s != y && e != y && n != y && d != y && m != y && o != y && r != y send = List(s, e, n, d).reduce(_ * 10 + _) more = List(m, o, r, e).reduce(_ * 10 + _) money = List(m, o, n, e, y).reduce(_ * 10 + _) if send + more == money } yield (send, more, money) Search candidate solutions Constraints satisfied?
24. val listOfDigits = (0 to 9).toList val solutions = for

{ s <- listOfDigits if s != 0 e <- listOfDigits if s != e n <- listOfDigits if s != n && e != n d <- listOfDigits if s != d && e != d && n != d m <- listOfDigits if m != 0 if s != m && e != m && n != m && d != m o <- listOfDigits if s != o && e != o && n != o && d != o && m != o r <- listOfDigits if s != r && e != r && n != r && d != r && m != r && o != r y <- listOfDigits if s != y && e != y && n != y && d != y && m != y && o != y && r != y send = List(s, e, n, d).reduce(_ * 10 + _) more = List(m, o, r, e).reduce(_ * 10 + _) money = List(m, o, n, e, y).reduce(_ * 10 + _) if send + more == money } yield (send, more, money)

26. None

S = …

0 S = 0!
31. None

S = …
33. 0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 5 1 2 3 4 0 6 7 8 9 0 6 2 3 4 5 1 7 8 9 0 1 7 3 4 5 6 2 8 9 0 1 3 2 4 5 6 7 8 9 0 1 8 3 4 5 6 7 8 9 0 1 4 3 2 5 6 7 8 9 0 1 9 3 4 5 6 7 8 2
34. 0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 5 1 2 3 4 0 6 7 8 9 0 6 2 3 4 5 1 7 8 9 0 1 7 3 4 5 6 2 8 9 0 1 3 2 4 5 6 7 8 9 0 1 8 3 4 5 6 7 8 9 0 1 4 3 2 5 6 7 8 9 0 1 9 3 4 5 6 7 8 2 S = 1

S = 1

S = 1 E = …
37. 0 1 2 3 4 5 6 7 8 9

S = 1 E = … 0 2 3 4 5 6 7 8 9

P. Robertson

A)
42. trait State[S, +A] { def eval(initial: S): A def map[B](f:

A => B): State[S, B] def flatMap[B](f: A => State[S, B]): State[S, B] } object State { def apply[S, A](f: S => (S, A)): State[S, A] }
43. // stateful computation f: S => (S, A) def select(xs:

List[Int]): (List[Int], Int) = (xs.tail, xs.head)
44. // stateful computation f: S => (S, A) def select(xs:

List[Int]): (List[Int], Int) = (xs.tail, xs.head)
45. // stateful computation f: S => (S, A) def select(xs:

List[Int]): (List[Int], Int) = (xs.tail, xs.head)
46. import scalaz.State val s = for { a <- State(select)

b <- State(select) } yield Map('a' -> a, 'b' -> b) val s = ???
47. import scalaz.State val s = for { a <- State(select)

b <- State(select) } yield Map('a' -> a, 'b' -> b) val s: State[ S, A ] = ???
48. import scalaz.State val s = for { a <- State(select)

b <- State(select) } yield Map('a' -> a, 'b' -> b) val s: State[ List[Int], Map[Char, Int] ] = ???
49. import scalaz.State val s = for { a <- State(select)

b <- State(select) } yield Map('a' -> a, 'b' -> b) val s: State[ List[Int], Map[Char, Int] ] = ??? s.eval(List(1, 2)) ==== Map('a' -> 1, 'b' -> 2)
50. import scalaz.State val s = for { a <- State(select)

b <- State(select) } yield Map('a' -> a, 'b' -> b) val s: State[ List[Int], Map[Char, Int] ] = ??? s.eval(List(8, 3)) ==== Map('a' -> 8, 'b' -> 3)
51. val s = for { a <- State(select) b <-

State(select) c <- State(select) } yield Map('a' -> a, 'b' -> b, 'c' -> c) s.eval(1 |-> 3) ==== Map('a' -> 1, 'b' -> 2, 'c' -> 3)
52. val s = for { a <- State(select) b <-

State(select) c <- State(select) d <- State(select) } yield Map('a' -> a, 'b' -> b, 'c' -> c, 'd' -> d) s.eval(1 |-> 4) ==== Map('a' -> 1, 'b' -> 2, 'c' -> 3, 'd' -> 4)

54. val l = for { a <- List(1, 2) b

<- List(1, 2) } yield Map('a' -> a, 'b' -> b) val l = ???
55. val l = for { a <- List(1, 2) b

<- List(1, 2) } yield Map('a' -> a, 'b' -> b) List(Map('a' -> 1, 'b' -> 1), Map('a' -> 1, 'b' -> 2), Map('a' -> 2, 'b' -> 1), Map('a' -> 2, 'b' -> 2))
56. val l = for { a <- List(1, 2) b

<- List(1, 2) if a != b } yield Map('a' -> a, 'b' -> b) List(Map('a' -> 1, 'b' -> 2), Map('a' -> 2, 'b' -> 1))
57. None

59. None
60. None

, A)
62. def select[A](xs: List[A]): (List[A], A) State S => ( S

, A) def select[A](xs: List[A]): List[(List[A], A)] StateT[F, S, A] S => F[( S , A)]
63. def select[A](xs: List[A]): List[(List[A], A)] = xs map { x

=> (xs.filterNot(_ == x), x) }
64. 0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 5 1 2 3 4 0 6 7 8 9 0 6 2 3 4 5 1 7 8 9 0 1 7 3 4 5 6 2 8 9 0 1 3 2 4 5 6 7 8 9 0 1 8 3 4 5 6 7 8 9 0 1 4 3 2 5 6 7 8 9 0 1 9 3 4 5 6 7 8 2
65. sl.eval(1 |-> 2) == ??? import scalaz.StateT import scalaz.std.list._ val

sl = for { a <- StateT(select) b <- StateT(select) } yield Map('a' -> a, 'b' -> b)
66. import scalaz.StateT import scalaz.std.list._ val sl = for { a

<- StateT(select) b <- StateT(select) } yield Map('a' -> a, 'b' -> b) sl.eval(1 |-> 2) == List(Map('a' -> 1, 'b' -> 2), Map('a' -> 2, 'b' -> 1))
67. val sel = StateT(select[Int]) for { s <- sel e

<- sel n <- sel d <- sel m <- sel o <- sel r <- sel y <- sel … }
68. val sel = StateT(select[Int]) for { s <- sel if

s != 0 e <- sel n <- sel d <- sel m <- sel if s != 0 o <- sel r <- sel y <- sel … }
69. val sel = StateT(select[Int]) for { s <- sel if

s != 0 e <- sel n <- sel d <- sel m <- sel if s != 0 o <- sel r <- sel y <- sel … } ?

73. class BooleanOps(self: Boolean) { // ... def guard[M[_]] = new

GuardPrevent[M] { def apply[A](a: => A) = b.pointOrEmpty[M, A](self)(a) } // ... }
74. class BooleanOps(self: Boolean) { // ... def guard[M[_]] = new

GuardPrevent[M] { def apply[A](a: => A) = b.pointOrEmpty[M, A](self)(a) } // ... } (implicit M: Applicative[M], M0: PlusEmpty[M])
75. class BooleanOps(self: Boolean) { // ... def guard[M[_]] = new

GuardPrevent[M] { def apply[A](a: => A) = b.pointOrEmpty[M, A](self)(a) } // ... } def pointOrEmpty[M[_], A](cond: Boolean)(a: => A): M[A] = if (cond) M.point(a) else M0.empty
76. class BooleanOps(self: Boolean) { // ... def guard[M[_]] = new

GuardPrevent[M] { def apply[A](a: => A) = b.pointOrEmpty[M, A](self)(a) } // ... } def pointOrEmpty[M[_], A](cond: Boolean)(a: => A): M[A] = if (cond) M.point(a) else M0.empty
77. val g = for { a <- StateT(selectSL[Int]) b <-

StateT(selectSL[Int]) c <- StateT(selectSL[Int]) } yield Map('a' -> a, 'b' -> b, 'c' -> c) g.eval(1 |-> 3) List( Map(a -> 1, b -> 2, c -> 3), Map(a -> 1, b -> 3, c -> 2), Map(a -> 2, b -> 1, c -> 3), Map(a -> 2, b -> 3, c -> 1), Map(a -> 3, b -> 1, c -> 2), Map(a -> 3, b -> 2, c -> 1) )
78. type StateL[A] = StateT[List, List[Int], A] val g = for

{ a <- StateT(selectSL[Int]) b <- StateT(selectSL[Int]) c <- StateT(selectSL[Int]) _ <-(a+b==c).guard[StateL](()) } yield Map('a' -> a, 'b' -> b, 'c' -> c) g.eval(1 |-> 3) List( Map(a -> 1, b -> 2, c -> 3), Map(a -> 2, b -> 1, c -> 3), )
79. type StateL[A] = StateT[List, List[Int], A] val g = for

{ a <- StateT(selectSL[Int]) b <- StateT(selectSL[Int]) c <- StateT(selectSL[Int]) _ <-(a+b==c).guard[StateL](()) } yield Map('a' -> a, 'b' -> b, 'c' -> c) g.eval(1 |-> 3) List( Map(a -> 1, b -> 2, c -> 3), Map(a -> 2, b -> 1, c -> 3), ) MonadPlus MonadPlus

81. type StateL[A] = StateT[List, List[Int], A] val g = for

{ a <- StateT(selectSL[Int]) b <- StateT(selectSL[Int]) c <- StateT(selectSL[Int]) _ <-(a+b==c).guard[StateL](()) _ <- (a>b).prevent[StateL](()) } yield Map('a' -> a, 'b' -> b, 'c' -> c) g.eval(1 |-> 3) List( Map(a -> 1, b -> 2, c -> 3), )
82. type StateL[A] = StateT[List, List[Int], A] val g = for

{ a <- StateT(selectSL[Int]) b <- StateT(selectSL[Int]) c <- StateT(selectSL[Int]) _ <-(a+b==c).guard[StateL](()) _ <- (a>b).prevent[StateL](()) } yield Map('a' -> a, 'b' -> b, 'c' -> c) g.eval(1 |-> 3) List( Map(a -> 1, b -> 2, c -> 3), ) flatMap

84. type StateL[A] = StateT[List, List[Int], A] def solutions: StateL[(Int, Int,

Int)] = ??? solutions.eval(0 |-> 9).head |> println
85. type StateL[A] = StateT[List, List[Int], A] def select[A](xs: List[A]): List[(List[A],

A)] = xs map { x => (xs.filterNot(_ == x), x) } def go(chars: List[Char], subst: Map[Char, Int])(i: Int): StateL[(Int, Int, Int)] = ??? def solutions: StateL[(Int, Int, Int)] = StateT(select[Int]) >>= go("sendmoremoney".toList.distinct, Map.empty) solutions.eval(0 |-> 9).head |> println
86. type StateL[A] = StateT[List, List[Int], A] def select[A](xs: List[A]): List[(List[A],

A)] = xs map { x => (xs.filterNot(_ == x), x) } def go(chars: List[Char], subst: Map[Char, Int])(i: Int): StateL[(Int, Int, Int)] = chars match { case c :: Nil => prune(subst + (c -> i)) case c :: cs => StateT(select[Int]) >>= go(cs, subst + (c -> i)) } def prune(charToDigit: Map[Char, Int]): StateL[(Int, Int, Int)] = ??? def solutions: StateL[(Int, Int, Int)] = StateT(select[Int]) >>= go("sendmoremoney".toList.distinct, Map.empty) solutions.eval(0 |-> 9).head |> println
87. type StateL[A] = StateT[List, List[Int], A] def select[A](xs: List[A]): List[(List[A],

A)] = xs map { x => (xs.filterNot(_ == x), x) } def go(chars: List[Char], subst: Map[Char, Int])(i: Int): StateL[(Int, Int, Int)] = chars match { case c :: Nil => prune(subst + (c -> i)) case c :: cs => StateT(select[Int]) >>= go(cs, subst + (c -> i)) } def prune(charToDigit: Map[Char, Int]): StateL[(Int, Int, Int)] = for { _ <- (charToDigit('m') == 0).prevent[StateL](()) _ <- (charToDigit('s') == 0).prevent[StateL](()) send = "send" map charToDigit reduce (_ * 10 + _) more = "more" map charToDigit reduce (_ * 10 + _) money = "money" map charToDigit reduce (_ * 10 + _) _ <- (send + more == money).guard[StateL](()) } yield (send, more, money) def solutions: StateL[(Int, Int, Int)] = StateT(select[Int]) >>= go("sendmoremoney".toList.distinct, Map.empty) solutions.eval(0 |-> 9).head |> println https://github.com/BartoszMilewski/MoreMoney/blob/master/Scala/src/main/scala/SendMoreMoney.scala
88. select ::[a] ->[(a, [a])] select [] = [] select(x:xs) =

(x, xs) : [(y, x:ys) | (y, ys) <-select xs] solve :: StateL [Int] (Int, Int, Int) solve = StateL select >>= go (nub "sendmoremoney") M.empty where go [c] subst i = prune (M.insert c i subst) go (c:cs) subst i = StateL select >>= go cs (M.insert c i subst) prune subst = do guard (get 's' /= 0 && get 'm' /= 0) let send = toNumber "send" more = toNumber "more" money = toNumber "money" guard \$ send + more == money return (send, more, money) where get c = fromJust (M.lookup c subst) toNumber str = asNumber (map get str) asNumber = foldl (\t o -> t*10 + o) 0 main = print \$ evalStateL solve [0..9] https://github.com/BartoszMilewski/MoreMoney/blob/master/Haskell/Advanced.hs
89. None
90. go [c] subst i = prune (M.insert c i subst)

go (c:cs) subst i = StateL select >>= go cs (M.insert c i subst) def go(chars: List[Char], subst: Map[Char, Int]) (i: Int) = chars match { case c :: Nil => prune(subst + (c -> i)) case c :: cs => StateT(select[Int]) >>= go(cs, subst + (c -> i)) }
91. def prune(charToDigit: Map[Char, Int]) = for { _ <- (charToDigit('m')

== 0).prevent[StateL](()) _ <- (charToDigit('s') == 0).prevent[StateL](()) send = "send" map charToDigit reduce (_ * 10 + _) more = "more" map charToDigit reduce (_ * 10 + _) money = "money" map charToDigit reduce (_ * 10 + _) _ <- (send + more == money).guard[StateL](()) } yield (send, more, money) prune subst = do guard (get 's' /= 0 && get 'm' /= 0) let send = toNumber "send" more = toNumber "more" money = toNumber "money" guard \$ send + more == money return (send, more, money) where get c = fromJust (M.lookup c subst) toNumber str = asNumber (map get str) asNumber = foldl (\t o -> t*10 + o) 0

93. - “The Essence of the Iterator Pattern” - Parallel Execution

- Different Algorithm - Parallel Genetic Algorithm - Solve it at compile time What’s next? FP-Syd
94. - “The Essence of the Iterator Pattern” - Parallel Execution

- Different Algorithm - Parallel Genetic Algorithm - Solve it at compile time FP-Syd scalaz.Traverse What’s next?
95. - “The Essence of the Iterator Pattern” - Parallel Execution

- Different Algorithm - Parallel Genetic Algorithm - Solve it at compile time FP-Syd scalaz.ApplicativePlus scalaz.Nondeterminism What’s next?
96. - “The Essence of the Iterator Pattern” - Parallel Execution

- Different Algorithm - Parallel Genetic Algorithm - Solve it at compile time FP-Syd (A + B % 10) = (C | 1C) What’s next?
97. - “The Essence of the Iterator Pattern” - Parallel Execution

- Different Algorithm - Parallel Genetic Algorithm - Solve it at compile time FP-Syd Cryptarithmetic problems What’s next?
98. - “The Essence of the Iterator Pattern” - Parallel Execution

- Different Algorithm - Parallel Genetic Algorithm - Solve it at compile time FP-Syd Peano's arithmetic Shapeless: HList, Sum, … What’s next?
99. - “The Essence of the Iterator Pattern” - Parallel Execution

- Different Algorithm - Parallel Genetic Algorithm - Solve it at compile time FP-Syd What’s next?
100. Resources - 4-part blog at http://BartoszMilewski.com - code: https://github.com/BartoszMilewski/MoreMoney -

code: https://gist.github.com/filippovitale/6cc45396ed917a2a8411 - Monad Transformers http://goo.gl/VhcJ65 - State Monad https://goo.gl/jt6bvz - EIP http://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf - http://jto.github.io/articles/typelevel_quicksort/ - Parallel Genetic Algorithm http://dl.acm.org/citation.cfm?id=1725467