Skip to content

Instantly share code, notes, and snippets.

@monzee
Last active February 15, 2017 01:17
Show Gist options
  • Save monzee/0e29e725b2566e4df6cd90bf623c71d0 to your computer and use it in GitHub Desktop.
Save monzee/0e29e725b2566e4df6cd90bf623c71d0 to your computer and use it in GitHub Desktop.
Monad interfaces for IO<T>, Option<T>, Either<L, R> and combined IO<Option<T>> (Job<T>) and IO<Either<L, R>> (Task<L,R>)
package m;
import java.util.function.*;
public class Monads {
static <T, U> Function<Option<T>, Option<U>>
lift(Function<T, U> t2u) {
return ot -> pu -> ot.map(t2u).match(pu);
}
static <T, U> Function<Option<T>, Option<U>>
liftOption(Function<T, Option<U>> t2ou) {
return ot -> pu -> ot.flatMap(t2ou).match(pu);
}
public static void main(String[] args) {
Option.some(100)
.map(n -> 10 * n)
.map(n -> "-> " + n)
.tee(printOption())
.<Integer>flatMap(s -> Option.none())
.map(n -> 1024 - n)
.match(printOption());
Io.unit(1048576)
.map(n -> n * 5)
.tee(printIo())
.flatMap(n -> Io.unit("wew"))
.tee((String s) -> out("hey it's a string: " + s))
.run(printIo());
Io.unit(Option.some("hello"))
.tee(o -> o.match(printOption()))
.map(o -> o.flatMap(n -> Option.none()))
.tee(o -> o.match(printOption()))
.map(ignored -> Option.some(100))
.tee(o -> o.match(printOption()))
.map(lift(n -> n * 25))
.tee(o -> o.match(printOption()))
.map(liftOption(n -> Option.some(n / 50)))
.tee(o -> o.match(printOption()))
.map(liftOption(n -> Option.<Integer>none()))
.<Option<String>>map(o -> next -> o.match(new Option.Of<Integer>() {
@Override public void some(Integer n) {
next.none();
}
@Override public void none() {
next.some("recovered");
}
}))
.tee(o -> o.match(printOption()))
.map(o -> o.map(String::toUpperCase))
.run(o -> o.match(printOption()));
Job.of(Option.some("hello"))
.tee(printOption())
.flatMap(n -> Option.none())
.tee(printOption())
.reset(Option.some(100))
.tee(printOption())
.map(n -> n * 25)
.tee(printOption())
.flatMap(n -> Option.some(n / 50))
.tee(printOption())
.<String>recover(o -> next -> o.match(new Option.Of<Integer>() {
@Override public void some(Integer n) {
next.none();
}
@Override public void none() {
next.some("recovered");
}
}))
.tee(printOption())
.map(String::toUpperCase)
.run(printOption());
Task.<RuntimeException, String>of(Either.ok("hello"))
.tee(printEither())
.<Integer>reset(Either.fail(new IllegalStateException("div by 0")))
.tee(printEither())
.<String>recover(prev -> next -> prev.match(new Either.Of<Throwable, Object>() {
@Override public void ok(Object v) {
next.ok("hi");
}
@Override public void fail(Throwable e) {
if (e instanceof ArithmeticException) {
next.ok("it's not a real error, it's fine.");
} else {
next.fail(new IllegalStateException("tried to recover"));
}
}
}))
.tee(printEither())
.flatMap(none -> Either.ok("recovered?"))
.map(String::toUpperCase)
.run(printEither());
out("");
}
static <T> Option.Of<T> printOption() {
return new Option.Of<T>() {
@Override public void some(T s) {
out("[something] " + s);
}
@Override public void none() {
out("[ nothing ]");
}
};
}
static <T> Io.Action<T> printIo() {
return t -> out("[io] " + t);
}
static <L, R> Either.Of<L, R> printEither() {
return new Either.Of<L, R>() {
@Override public void ok(R value) {
out("[ok] " + value);
}
@Override public void fail(L error) {
out("[error] " + error);
}
};
}
static void out(Object msg) {
System.out.println("" + msg);
}
}
interface Task<E, V> {
static <E, V> Task<E, V> of(Either<E, V> eev) {
return aeev -> aeev.got(eev);
}
void run(Io.Action<Either<E, V>> aeev);
default <VV> Task<E, VV> reset(Either<E, VV> eevv) {
return recover(ignored -> eevv);
}
default <VV> Task<E, VV> recover(Function<Either<E, V>, Either<E, VV>> eev2eevv) {
return aeevv -> run(eev -> eev2eevv.apply(eev).match(new Either.Of<E, VV>() {
@Override public void ok(VV vv) {
aeevv.got(Either.ok(vv));
}
@Override public void fail(E error) {
aeevv.got(Either.fail(error));
}
}));
}
default <VV> Task<E, VV> map(Function<V, VV> v2vv) {
return recover(eev -> eev.map(v2vv));
}
default <VV> Task<E, VV> flatMap(Function<V, Either<E, VV>> v2eevv) {
return recover(eev -> eev.flatMap(v2eevv));
}
default Task<E, V> tee(Either.Of<E, V> matcher) {
run(matcher);
return this;
}
default void run(Either.Of<E, V> matcher) {
run(eev -> eev.match(matcher));
}
}
interface Job<T> {
static <T> Job<T> of(Option<T> ot) {
return aot -> aot.got(ot);
}
void run(Io.Action<Option<T>> aot);
default <U> Job<U> reset(Option<U> ou) {
return recover(ignored -> ou);
}
default <U> Job<U> recover(Function<Option<T>, Option<U>> ot2ou) {
return aou -> run(ot -> ot2ou.apply(ot).match(new Option.Of<U>() {
@Override public void some(U u) {
aou.got(Option.some(u));
}
@Override public void none() {
aou.got(Option.none());
}
}));
}
default <U> Job<U> map(Function<T, U> t2u) {
return recover(ot -> ot.map(t2u));
}
default <U> Job<U> flatMap(Function<T, Option<U>> t2ou) {
return recover(ot -> ot.flatMap(t2ou));
}
default Job<T> tee(Option.Of<? super T> matcher) {
run(matcher);
return this;
}
default void run(Option.Of<? super T> matcher) {
run(ot -> ot.match(matcher));
}
}
interface Io<T> {
interface Action<T> {
void got(T value);
}
void run(Action<? super T> action);
static <T> Io<T> unit(T value) {
return action -> action.got(value);
}
default <U> Io<U> map(Function<T, U> t2u) {
return au -> run(t -> au.got(t2u.apply(t)));
}
default <U> Io<U> flatMap(Function<T, Io<U>> t2mu) {
return au -> run(t -> t2mu.apply(t).run(au));
}
default Io<T> tee(Action<? super T> action) {
run(action);
return this;
}
}
interface Option<T> {
interface Of<T> {
void some(T value);
void none();
}
void match(Of<? super T> matcher);
static <T> Option<T> some(T value) {
return matcher -> matcher.some(value);
}
static <T> Option<T> none() {
return matcher -> matcher.none();
}
default Option<T> tee(Of<? super T> matcher) {
match(matcher);
return this;
}
default <U> Option<U> map(Function<T, U> t2u) {
return pu -> match(new Of<T>() {
@Override public void some(T t) {
pu.some(t2u.apply(t));
}
@Override public void none() {
pu.none();
}
});
}
default <U> Option<U> flatMap(Function<T, Option<U>> t2mu) {
return pu -> match(new Of<T>() {
@Override public void some(T t) {
t2mu.apply(t).match(pu);
}
@Override public void none() {
pu.none();
}
});
}
}
interface Either<L, R> {
interface Of<L, R> {
void ok(R result);
void fail(L error);
}
void match(Of<? super L, ? super R> matcher);
static <L, R> Either<L, R> ok(R value) {
return matcher -> matcher.ok(value);
}
static <L, R> Either<L, R> fail(L error) {
return matcher -> matcher.fail(error);
}
default Either<L, R> tee(Of<? super L, ? super R> matcher) {
match(matcher);
return this;
}
default <RR> Either<L, RR> map(Function<R, RR> r2rr) {
return rrMatcher -> match(new Of<L, R>() {
@Override public void ok(R value) {
rrMatcher.ok(r2rr.apply(value));
}
@Override public void fail(L error) {
rrMatcher.fail(error);
}
});
}
default <RR> Either<L, RR> flatMap(Function<R, Either<L, RR>> r2mrr) {
return rrMatcher -> match(new Of<L, R>() {
@Override public void ok(R value) {
r2mrr.apply(value).match(rrMatcher);
}
@Override public void fail(L error) {
rrMatcher.fail(error);
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment