Let
A,B,Cbe typesunit: A -> Monad<A>a constructorf: A -> Monad<B>,g: B -> Monad<C>functionsabe an object of typeAmbe an object of typeMonad<A>
Then all instances of the Monad interface should obey the three control laws:
- Left identity:
unit(a).flatMap(f) ≡ f a - Right identity:
m.flatMap(unit) ≡ m - Associativity:
m.flatMap(f).flatMap(g) ≡ m.flatMap(x -> f.apply(x).flatMap(g))
Monads in Javaslang
Strictly, given a Monad type Monad, the signature of the flatMap method should look like this in Java. For the sake of simplicity we omitted other interface methods like Functor.map.
interface Monad<A> {
<B> Monad<B> flatMap(Function<? super A, ? extends Monad<? extends B>> f);
}Example:
interface Try<A> extends Monad<A> {
<B> Try<B> flatMap(Function<? super A, ? extends Try<? extends B>> f);
}Because of the lack of higher-kinded types we cannot define the Try interface as shown above. It does not compile because we changed the function return type from ? extends Monad to ? extends Try. To circumvent this limitation we relaxed the Monad interface a bit:
interface Monad<A> extends Iterable<A> {
<B> Monad<B> flatMap(Function<? super A, ? extends Iterable<? extends B>> f);
}We are now able to define Try like this:
interface Try<A> extends Monad<A> {
<B> Try<B> flatMap(Function<? super A, ? extends Iterable<? extends B>> f);
}The Monad implementations are still satisfying the (adapted) Monad laws.
Examples:
// = Some(0.1)
Option.of(10).flatMap(i -> Try.of(() -> 1.0 / i));
// = List(-1, 1)
List.ofAll(-2, 0, 2).flatMap(i -> Try.of(() -> 2 / i));
// = TreeSet(1, 2, 3, 4)
TreeSet.ofAll(3, 2, 1).flatMap(i -> List.ofAll(i, i + 1));
// = Failure(java.lang.ArithmeticException: / by zero)
Monad.lift((Integer a, Integer b) -> a / b)
.apply(Try.success(20), List.ofAll(0, 4, 1, 2, 3));
// = Success(5)
Monad.lift((Integer a, Integer b) -> a / b)
.apply(Try.success(20), List.ofAll(4, 1, 2, 3));
// = List(5, 20, 10, 6)
Monad.lift((Integer a, Integer b) -> b / a)
.apply(List.ofAll(0, 4, 1, 2, 3), Try.success(20));
When you say:
Why does f's return type have to be
Monad<? extends B>? Is it for expressing covariance? Shouldn't beMonad<B>enough in Java? The way you wrote it it seems that an instance of exactly B wouldn't pass type checks... (only objects extending B allowed) Am I wrong? (probably :/ )