send + more = money – let’s mash 2 monads to solve a simple csp

102
July 2015 @filippovitale Send + More = Money Let’s merge 2 monads to solve a simple “constraint satisfaction problem”

Upload: filippo-vitale

Post on 14-Aug-2015

15 views

Category:

Software


2 download

TRANSCRIPT

July 2015

@filippovitale

Send + More = MoneyLet’s merge 2 monads to solve a simple

“constraint satisfaction problem”

We’re hiring!

@alexeyraga

SEND+ MORE------- MONEY

@alexeyraga

SEND+ MORE------- MONEY

@alexeyraga

SEND+ MORE------- MONEY

@alexeyraga

SEND+ MORE------- MONEY

SEND+ MORE------- MONEY

SEND+ MORE------- MONEY

M←1

SEND+ MORE------- MONEY

M←1

SEND+ MORE------- MONEY

M←1

SEND+ 1ORE------- 1ONEY

M←1

O←0S←8,9

M←1Hold on a sec…

@alexeyraga

M←1Programmatically!

@alexeyraga

Immutability

Higher-ord

er

function Monad

Referentia

l

transparen

cy

Mon*d

Why Monads- Higher order control structures- Monads : loops = loops : gotos- Declarative vs. Imperative- Reusable ⊛ Composable ⊛ Factorisable- Easily parallelisable

Brute force

10!/(10 – 8)!≈ 2 Million

How big is the search space?

Brute ForceNaïve implementation

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 != 0e != sn != s && n != ed != s && d != e && d != n

The solution boils down to:➔ generating all those substitutions➔ testing the constraints for each one

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

Constraintssatisfied?

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)

“Jimmy” and theMulti-verse

0 1 2 3

4 5 6 7

8 9

1 2 3

4 5 6 7

8 9

0

? ? ?

? ? ? ?

? ?

?

S = …

1 2 3

4 5 6 7

8 9

0

0

S = 0!

? ? ?

? ? ? ?

? ?

?

S = …

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

32

5 6 7

8 9

0 1

9

3

4 5 6 7

8

2

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

32

5 6 7

8 9

0 1

9

3

4 5 6 7

8

2

S = 1

0

1

2 3

4 5 6 7

8 9

S = 1

0

1

2 3

4 5 6 7

8 9

S = 1E = …

0

1

2 3

4 5 6 7

8 9

S = 1E = …

0

2

3

4

5

6

789

“What would Tony Morris do?”

Multi-verse Implementation

using the State Monad

“The future is a function of the past.”– A. P. Robertson

// stateful computation

type State[S, +A] = S => (S, A)

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

// stateful computation

f: S => (S, A)

def select(xs: List[Int]): (List[Int], Int) = (xs.tail, xs.head)

// stateful computation

f: S => (S, A)

def select(xs: List[Int]): (List[Int], Int) = (xs.tail, xs.head)

// stateful computation

f: S => (S, A)

def select(xs: List[Int]): (List[Int], Int) = (xs.tail, xs.head)

import scalaz.State

val s = for { a <- State(select) b <- State(select)} yield Map('a' -> a, 'b' -> b)

val s = ???

import scalaz.State

val s = for { a <- State(select) b <- State(select)} yield Map('a' -> a, 'b' -> b)

val s: State[ S, A ] = ???

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] ] = ???

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)

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)

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)

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)

Multi-verse Implementation

using List

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

val l = ???

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

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

Multi-verse Implementation

using StateT[List, S, A]

def select[A](xs: List[A]): (List[A], A)

State S => ( S , A)

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

def select[A](xs: List[A]): List[(List[A], A)] =

xs map { x => (xs.filterNot(_ == x), x) }

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

32

5 6 7

8 9

0 1

9

3

4 5 6 7

8

2

sl.eval(1 |-> 2)

==

???

import scalaz.StateTimport scalaz.std.list._

val sl = for { a <- StateT(select) b <- StateT(select)} yield Map('a' -> a, 'b' -> b)

import scalaz.StateTimport 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))

val sel = StateT(select[Int])

for { s <- sel e <- sel n <- sel d <- sel m <- sel o <- sel r <- sel y <- sel …}

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

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

?

Guards via MonadPlus

Functor

Applicative

Monad

MonadPlus

trait PlusEmpty[F[_]] {

def empty[A]: F[A]

// ...}

class BooleanOps(self: Boolean) {// ...

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

// ...}

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

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

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

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

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

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

MonadPlusMonadPlus

scala> monadPlus.laws[List].check+ monad plus.monad.applicative.apply.functor.invariantFunctor.identity: OK, passed 100 tests.+ monad plus.monad.applicative.apply.functor.invariantFunctor.composite: OK, passed 100 tests.+ monad plus.monad.applicative.apply.functor.identity: OK, passed 100 tests.+ monad plus.monad.applicative.apply.functor.composite: OK, passed 100 tests.+ monad plus.monad.applicative.apply.composition: OK, passed 100 tests.+ monad plus.monad.applicative.identity: OK, passed 100 tests.+ monad plus.monad.applicative.homomorphism: OK, passed 100 tests.+ monad plus.monad.applicative.interchange: OK, passed 100 tests.+ monad plus.monad.applicative.map consistent with ap: OK, passed 100 tests.+ monad plus.monad.bind.apply.functor.invariantFunctor.identity: OK, passed 100 tests.+ monad plus.monad.bind.apply.functor.invariantFunctor.composite: OK, passed 100 tests.+ monad plus.monad.bind.apply.functor.identity: OK, passed 100 tests.+ monad plus.monad.bind.apply.functor.composite: OK, passed 100 tests.+ monad plus.monad.bind.apply.composition: OK, passed 100 tests.+ monad plus.monad.bind.associativity: OK, passed 100 tests.+ monad plus.monad.bind.ap consistent with bind: OK, passed 100 tests.+ monad plus.monad.right identity: OK, passed 100 tests.+ monad plus.monad.left identity: OK, passed 100 tests.+ monad plus.plusEmpty.plus.semigroup.associative: OK, passed 100 tests.+ monad plus.plusEmpty.plus.associative: OK, passed 100 tests.+ monad plus.plusEmpty.monoid.semigroup.associative: OK, passed 100 tests.+ monad plus.plusEmpty.monoid.left identity: OK, passed 100 tests.+ monad plus.plusEmpty.monoid.right identity: OK, passed 100 tests.+ monad plus.plusEmpty.left plus identity: OK, passed 100 tests.+ monad plus.plusEmpty.right plus identity: OK, passed 100 tests.+ monad plus.empty map: OK, passed 100 tests.+ monad plus.left zero: OK, passed 100 tests.

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

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

The FinalImplementation

type StateL[A] = StateT[List, List[Int], A]

def solutions: StateL[(Int, Int, Int)] = ???

solutions.eval(0 |-> 9).head |> println

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

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

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

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

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

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

What’s next?

- “The Essence of the Iterator Pattern”- Parallel Execution- Different Algorithm- Parallel Genetic Algorithm - Solve it at compile time

What’s next?

FP-Syd

- “The Essence of the Iterator Pattern”- Parallel Execution- Different Algorithm- Parallel Genetic Algorithm - Solve it at compile time

FP-Syd

scalaz.TraverseWhat’s next?

- “The Essence of the Iterator Pattern”- Parallel Execution- Different Algorithm- Parallel Genetic Algorithm - Solve it at compile time

FP-Syd

scalaz.ApplicativePlusscalaz.Nondeterminism

What’s next?

- “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?

- “The Essence of the Iterator Pattern”- Parallel Execution- Different Algorithm- Parallel Genetic Algorithm - Solve it at compile time

FP-SydCryptarithmetic problems

What’s next?

- “The Essence of the Iterator Pattern”- Parallel Execution- Different Algorithm- Parallel Genetic Algorithm - Solve it at compile time

FP-SydPeano's arithmetic

Shapeless: HList, Sum, …

What’s next?

- “The Essence of the Iterator Pattern”- Parallel Execution- Different Algorithm- Parallel Genetic Algorithm - Solve it at compile time

FP-Syd

What’s next?

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

July 2015

@filippovitale

Thanks!

July 2015

@filippovitale

$ tail -f questions