Last active
February 15, 2017 01:17
-
-
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>)
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 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