Skip to content

Instantly share code, notes, and snippets.

@danieldietrich
Last active August 11, 2024 20:56
Show Gist options
  • Save danieldietrich/a21f4de7a42052bf30ad to your computer and use it in GitHub Desktop.
Save danieldietrich/a21f4de7a42052bf30ad to your computer and use it in GitHub Desktop.
The most sophisticated generic Java type I ever wrote
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* The Monad interface.
*
* @param <M> The type of the monad, e.g. {@code class Foo<A> extends Monad<Foo<?>, A, Foo<A>>}.
* @param <A> Component type of {@code M}.
*/
public interface Monad<M extends Monad<?, ?>, A> {
// the constructor:
// <B> Monad<?, B> unit(B b);
// map(f) ~ unit(f.apply(a))
<B> Monad<?, B> map(Function<? super A, ? extends B> f);
// flatMap(f) ~ f.apply(a)
<B, MONAD extends Monad<M, B>> M flatMap(Function<? super A, MONAD> f);
}
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public final class None<T> implements Option<T> {
private static final None<?> INSTANCE = new None<>();
private None() {
}
@Override
public <U> Option<U> map(Function<? super T, ? extends U> mapper) {
return None.instance();
}
@Override
public <U, OPTION extends Monad<Option<?>, U>> Option<U> flatMap(Function<? super T, OPTION> mapper) {
return None.instance();
}
public static <T> None<T> instance() {
@SuppressWarnings("unchecked")
final None<T> none = (None<T>) INSTANCE;
return none;
}
}
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public interface Option<T> extends Monad<Option<?>, T> {
@Override
<U> Option<U> map(Function<? super T, ? extends U> mapper);
@Override
<U, OPTION extends Monad<Option<?>, U>> Option<U> flatMap(Function<? super T, OPTION> mapper);
static <T> Option<T> empty() {
return None.instance();
}
}
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public final class Some<T> implements Option<T> {
private final T value;
public Some(T value) {
this.value = value;
}
@Override
public <U> Option<U> map(Function<? super T, ? extends U> mapper) {
return new Some<>(mapper.apply(value));
}
@SuppressWarnings("unchecked")
@Override
public <U, OPTION extends Monad<Option<?>, U>> Option<U> flatMap(Function<? super T, OPTION> mapper) {
return (Option<U>) mapper.apply(value);
}
}
class Tests {{
final Option<String> ok1 = Option.empty();
final Option<String> ok2 = new Some<>("");
final Option<String> ok3 = None.instance();
final Option<Integer> ok4 = ok1.flatMap(s -> new Some<Integer>(1));
final Function<String, Some<Integer>> f = s -> new Some<>(s.length());
final Option<? extends Number> ok5 = ok1.flatMap(f);
// failing, ok!
final Option<Number> fail1 = ok1.flatMap(f);
// failing, ok!
final Some<Integer> fail2 = ok1.flatMap(s -> new Some<Integer>(1));
}}
@danieldietrich
Copy link
Author

Summarizing the solution, the interface method flatMap

interface Monad<M extends Monad<?,?>, A> {
    <B, MONAD extends Monad<M, B>> M flatMap(Function<? super A, MONAD> f);
}

is overridden with

interface Option<T> extends Monad<Option<?>, T> {
    @Override
    <U, OPTION extends Monad<Option<?>, U>> Option<U> flatMap(Function<? super T, OPTION> f);
}

We specify the return type MONAD of the function f, which is passed to flatMap. Because MONAD itself is parameterized with a generic parameter, we need to specify the container type M(e.g. Option<?>) and the component type A of M (e.g. T in the case of Option). By contract MONAD is of generic type M (informally M<B>), which is the return type of flatMap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment