Purely Functional State
Functional Programming in Java
Created by Mark Perry,
@mprry,
G+,
Blog,
LinkedIn,
GitHub,
maperry78@yahoo.com.au
Introduction
- Learn to manipulate state using RNGs
- Explore issues
- Pattern of any stateful API
Standard RNG
- java.util.Random has imperative API
- Uses side effects
Random rng = new java.util.Random();
rng.nextInt();
rng.nextInt();
rng.nextInt(5);
rng.nextInt(5);
Not referentially transparent
Harder to test, compose, modularise and parallelise
Testability
int rollDie() {
Random rng = new java.util.Random();
return rng.nextInt(6);
}
- Off by one error
- Obvious - complicated defects are subtle
- Need to reproduce reliably
Fixing the RNG?
int rollDie(Random rng) {
return rng.nextInt(6);
}
Generator needs same seed and call sequence to reproduce
Keep track of call sequence?
Answer: Reject side-effects!
Functional RNG
- Make the updates explicit!
- Do not mutate RNG
- Return new state as result
- Separate state computation from call sequence
class Rng {
P2<Rng, Integer> nextInt();
P2<Rng, Integer> nextInt(int max); // [0, max - 1]
P2<Rng, Long> nextLong();
P2<Rng, Double> nextDouble();
}
RNG Implementation
class LcgRng {
private long seed;
private LcgRng(long seed) { ... }
static Rng rng(long seed) { ... }
P2<Rng, Long> nextLong() {
long newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL;
long n = (Long) (newSeed >>> 16);
return P.p(n, rng(newSeed));
}
}
Rng r = LcgRng.rng(10);
P2<Rng, Integer> p = r.nextLong();
r.nextLong()._2();
r.nextLong()._2();
Rng r2 = r.nextLong()._1();
r2.nextLong()._2();
RNG Diagram
Pure Stateful APIs
- Make API return next state
- Use same technique
class Foo {
FooState state;
Bar bar();
int baz();
}
class Foo2 {
P2<FooState, Bar> bar(FooState fs);
P2<FooState, Int> baz(FooState fs);
}
Using the API
P2<Rng, P2<Long, Long>> randomPair(Rng rng) {
P2<Rng, Long> t1 = rng.nextLong();
P2<Rng, Long> t2 = t1._1().nextLong();
return p(t2._1(), p(t1._2(), t2._2()));
}
- Direct use will be tedious
- Refactor repetition
- Pattern of generating next state
State Abstracton
State Class
class State<S, A> {
F<S, P2<S, A>> run;
static <S, A> State<S, A> unit(F<S, P2<S, A>> f) {...}
P2<S, A> run(S s);
A eval(S s);
S exec(S s);
}
Example Usage
Rng r = LcgRng.rng(10);
State<Rng, Long> s1 = State.unit(rng -> rng.nextLong());
P2<Rng, Long> p = s1.run(r);
Long l = s1.eval(r); // 3847489
Rng r2 = s1.exec(r);
// use sequence to get a list of random longs
// static <S, A, B> State<S, List<A>> sequence(List<State<S, A>> list);
List<Long> nums = sequence(List.replicate(10, s1)).eval(r);
Exercises
static <S, A> State<S, A> constant(A a);
static <S, A, B> State<S, B> map(State<S, A> s, F<A, B> f);
static <S, A, B, C> State<S, C> map2(State<S, A> s1, State<S, B> s2, F2<A, B, C> f);
static <S, A, B> State<S, B> flatMap(State<S, A> s, F<A, State<S, B>> f);
static <S, A, B> State<S, List<A>> sequence(List<State<S, A>> list);
static <S, A, B> State<S, List<B>> traverse(List<A> list, F<A, State<S, B>> f);
Imperative Programming
- Sequence of statements modifying program state
- Statements are state transitions
- State is every reachable object
- Contrast: Limited immutable state with RT
Exercises
static <S> State<S, Unit> set(S s);
static <S> State<S, S> get();
static <S, A> State<S, Unit> modify(State<S, A> s, F<S, S> f);
static <S> State<S, S> init();
Challenge - Vending Machine
 |
- A vending machine has items to dispense and a number of coins inside from previous usage.
- It is initially locked.
- When a coin is inserted the machine unlocks and the user opens the door to get their item.
- After being successfully opened the machine is locked again.
|
Summary
- FP programs with state
- RNG example to motivate
- Use function that takes state and returns new state and a result
- State functions make this simple
The FP Road
Introduction
Data Structures
Errors without exceptions
Strictness and Laziness
Purely Functional State
Purely Functional Parrallelism
Property Based Testing
|
Parser Combinators
Monoids
Monads
Applicative Functors
External Effects and I/O
Local Effects and Mutable State
Stream Processing and Incremental I/O
|
Afterword
- Functional Programming in Scala, Chiusano and Bjarnason
- Chapter 6, Purely Functional State
Created by Mark Perry,
@mprry,
G+,
Blog,
LinkedIn,
GitHub,
maperry78@yahoo.com.au