Building Enigma with State Monad & Lens

Post on 10-Jul-2015

484 views 1 download

Transcript of Building Enigma with State Monad & Lens

Building Enigma with

State Monad & Lens

Timothy Perrett

SF Scala, December 2014

Hello.

73 years today.

Steckerbrett.(Plugboard)

Walzen. (Rotor)

Umkehrwalze.(Reflector)

Process.

158,962,555,217,826,360,000.(158 quintillion)

Process.

๏ Distributed with typically a month of configuration settings.

๏ Kriegsmarine vessels often had two Enigmas onboard to account for delivery delay due to being submersed

Demo.

Design.

Design.

Design.

Char => Char

Char => Char

Char => CharChar => Char

Code.

case class Plugboard(shuffled: Seq[Char]){ ... }

case class Rotor(

wiring: String,

ring: Char,

notch: Char,

posistion: Char

){ ... }

case class Machine(

plugboard: Plugboard,

right: Rotor,

middle: Rotor,

left: Rotor,

reflector: Reflector

){ ... }

Algebra.

for {

_ <- get[Machine]

_ <- modify((m: Machine) => right(m))

_ <- modify((m: Machine) => middle(m))

_ <- modify((m: Machine) => left(m))

o <- get[Machine]

} yield

o.plugboard.transform _ andThen

rtl(o) andThen

o.reflector.transform andThen

ltr(o) andThen o.plugboard.transform apply(c)

Program.

State.

S => (S, A)

๏ Given a state S, compute a resulting S(i.e. make any needed modifications to the state) and produce a resulting A

๏ Explicit state handling in every type signature can be tedious.

trait State[S,A] {

def get[S]: State[S,S]

def put[S](s: S): State[S, Unit]

def modify[S](f: S => S): State[S, Unit]

def run(s: S): (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] =

new State[S,A]{

def run(s: S): (S,A) = f(s)

}

}

State.

trait State[S,A] {

def run(s: S): (S,A)

def map[B](f: A => B): State[S,B] = State { s =>

val (x,a) = run(s)

(x,f(a))

}

def flatMap[B](f: A => State[S, B]): State[S,B] =

State { s =>

val (x,a) = run(s)

f(a).run(s)

}

}

State.

State.

type State[S,A]

๏ State monad threads S through your computation for you.

๏ Actual implementation in Scalaz is in terms of the monad transformer for State through Id.

๏ Avoid overflow by using: type State[S,A] = StateT[Trampoline,S,A]

for {

_ <- get[Machine]

_ <- modify((m: Machine) => right(m))

_ <- modify((m: Machine) => middle(m))

_ <- modify((m: Machine) => left(m))

o <- get[Machine]

} yield

o.plugboard.transform _ andThen

rtl(o) andThen

o.reflector.transform andThen

ltr(o) andThen o.plugboard.transform apply(c)

Program.

def stepRotor(r: Rotor): Rotor =

rotorL.modify(r, Alphabet.nextLetter)

def right(m: Machine): Machine =

m |-> rightL modify(stepRotor)

def middle(m: Machine): Machine =

m |-> middleL modify(r =>

if(m.right.notch == r.position ||

m.left.notch == r.position) stepRotor(r)

else r)

def left(m: Machine): Machine =

m |-> leftL modify(r =>

if(r.position == m.middle.notch) stepRotor(r)

else r)

Program.

Lens.

get: A => B

set: (A,B) => A

๏ Comprised of two functions: “getter” and “setter”, where the latter returns the updated value.

๏ Using lenses, updates compose

m.copy(

right = m.right.copy(

position = m.right.position + 1

),

middle = m.middle.copy(

position = m.middle.position + 1

),

left = m.left.copy(

position = m.left.position + 1

)

)

Lens.

case class Lens[A,B](

get: A => B,

set: (A, B) => A

)

val rotorL = Lens[Rotor, Char](

_.position,

(a, b) => a.copy(position = b)

)

rotorL.set(m.right)

Lens.

Lens.

type Lens[A,B]

๏ Build modular functions to composeupdates to complex domain types.

๏ Scalaz ships with a Lens implementation, but Monocle is really where its at for Lenses

Monocle.

๏ Scalaz Lens is great, but has boilerplate

๏ Monocle removes said boilerplate by using macros

๏ Provides powerful abstractions in the same space as Lens (Prism, Iso, Getter)

๏ Convenient syntax for bedazzlement of lenses

https://github.com/julien-truffaut/Monocle

val lenserM = Lenser[Machine]

val rightL = lenserM(_.right)

val middleL = lenserM(_.middle)

val leftL = lenserM(_.left)

val lenserR = Lenser[Rotor]

val rotorL: Lens[Rotor,Rotor,Char,Char] =

lenserR(_.position)

m |-> rightL |-> rotorL modify(_ + 1)

Monocle.

Roundup.

๏ FP gives you a frame to reason about problems in a straight-forward, explicit and easy manner.

๏ State monad gives you explicit, immutable control over state changes

๏ Lenses make updating nested domain types convenient and composable.

๏ Enigma was an incredible piece of engineering in 1918!

EOF

@timperrett

github.com/timperrett/enigma

timperrett.com