Created
November 2, 2023 18:57
-
-
Save xpmatteo/070dbe79819afc04cbba6e3f415a6bda to your computer and use it in GitHub Desktop.
Recreating in vanilla Java the State monad example from https://en.wikibooks.org/wiki/Haskell/Understanding_monads/State
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package it.xpug.spike.monads.monad; | |
import org.junit.jupiter.api.Test; | |
import java.util.List; | |
import java.util.function.Function; | |
import static it.xpug.spike.monads.monad.TurnstileTest.TurnstileOutput.*; | |
import static it.xpug.spike.monads.monad.TurnstileTest.TurnstileState.LOCKED; | |
import static it.xpug.spike.monads.monad.TurnstileTest.TurnstileState.UNLOCKED; | |
import static org.assertj.core.api.Assertions.assertThat; | |
record Pair<A, B>(A left, B right) { | |
public static <A,B> Pair<A, B> of(A left, B right) { | |
return new Pair<>(left, right); | |
} | |
} | |
// from https://en.wikibooks.org/wiki/Haskell/Understanding_monads/State | |
public class TurnstileTest { | |
static class State<S, A> { | |
private Function<S, Pair<A, S>> runState; | |
public State(Function<S, Pair<A, S>> runState) { | |
this.runState = runState; | |
} | |
public static <S, A> State<S, A> unit(A a) { | |
return new State<>(s -> Pair.of(a, s)); | |
} | |
public <B> State<S, B> bind(Function<A, State<S, B>> other) { | |
// apply both this.f and other.f and return the second value and the third state | |
Function<S, Pair<B, S>> newF = s0 -> { | |
// Running the first processor on s0 | |
Pair<A, S> firstApply = this.runState.apply(s0); | |
A valueReturnedByFirst = firstApply.left(); | |
S s1 = firstApply.right(); | |
// Running the second processor on s1 | |
State<S, B> second = other.apply(valueReturnedByFirst); | |
return second.runState.apply(s1); | |
}; | |
return new State(newF); | |
} | |
} | |
enum TurnstileState {LOCKED, UNLOCKED}; | |
enum TurnstileOutput {Thank, Open, Tut}; | |
Function<TurnstileState, Pair<TurnstileOutput, TurnstileState>> coin = __ -> Pair.of(Thank, UNLOCKED); | |
Function<TurnstileState, Pair<TurnstileOutput, TurnstileState>> push = s -> | |
s == LOCKED ? Pair.of(Tut, LOCKED) : Pair.of(Open, LOCKED); | |
List<TurnstileOutput> monday(TurnstileState s0) { | |
var p1 = coin.apply(s0); | |
var p2 = push.apply(p1.right()); | |
var p3 = push.apply(p2.right()); | |
var p4 = coin.apply(p3.right()); | |
var p5 = push.apply(p4.right()); | |
return List.of(p1.left(), p2.left(), p3.left(), p4.left(), p5.left()); | |
} | |
@Test | |
void withoutMonad() { | |
assertThat(monday(LOCKED)).isEqualTo(List.of(Thank, Open, Tut, Thank, Open)); | |
} | |
State<TurnstileState, TurnstileOutput> coinS = new State(coin); | |
State<TurnstileState, TurnstileOutput> pushS = new State(push); | |
@Test | |
void runState() { | |
assertThat(coinS.runState.apply(LOCKED)).isEqualTo(Pair.of(Thank, UNLOCKED)); | |
assertThat(pushS.runState.apply(LOCKED)).isEqualTo(Pair.of(Tut, LOCKED)); | |
assertThat(pushS.runState.apply(UNLOCKED)).isEqualTo(Pair.of(Open, LOCKED)); | |
} | |
List<TurnstileOutput> mondayS(TurnstileState s0) { | |
State<TurnstileState, List<TurnstileOutput>> boh = | |
coinS.bind(a1 -> | |
pushS.bind(a2 -> | |
pushS.bind(a3 -> | |
coinS.bind(a4 -> | |
pushS.bind(a5 -> | |
State.unit(List.of(a1, a2, a3, a4, a5))))))); | |
return boh.runState.apply(s0).left(); | |
} | |
@Test | |
void withMonad() { | |
assertThat(mondayS(LOCKED)).isEqualTo(List.of(Thank, Open, Tut, Thank, Open)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment