Skip to content

Commit 9c1c6d4

Browse files
committed
Adding Monad#join, alias of flatMap(id())
1 parent 7f23b8f commit 9c1c6d4

File tree

3 files changed

+36
-8
lines changed

3 files changed

+36
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
1414
- `SortWith` for sorting an `Iterable` given a `Comparator` over its elements
1515
- `IO#externallyManaged`, for supplying an `IO` with externally-managed futures
1616
- test jar is now published
17+
- `Monad#join` static alias for `flatMap(id())`
1718

1819
### Fixed
1920
- issue where certain ways to compose `Effect`s unintentionally nullified the effect

src/main/java/com/jnape/palatable/lambda/monad/Monad.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package com.jnape.palatable.lambda.monad;
22

3+
import com.jnape.palatable.lambda.functions.builtin.fn1.Id;
34
import com.jnape.palatable.lambda.functor.Applicative;
45

56
import java.util.function.Function;
67

8+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
9+
710
/**
811
* Monads are {@link Applicative} functors that support a flattening operation to unwrap <code>M&lt;M&lt;A&gt;&gt;
912
* -&gt; M&lt;A&gt;</code>. This flattening operation, coupled with {@link Applicative#zip(Applicative)}, gives rise to
@@ -53,7 +56,7 @@ default <B> Monad<B, M> fmap(Function<? super A, ? extends B> fn) {
5356
*/
5457
@Override
5558
default <B> Monad<B, M> zip(Applicative<Function<? super A, ? extends B>, M> appFn) {
56-
return appFn.<Monad<Function<? super A, ? extends B>, M>>coerce().flatMap(ab -> fmap(ab::apply));
59+
return appFn.<Monad<Function<? super A, ? extends B>, M>>coerce().flatMap(this::fmap);
5760
}
5861

5962
/**
@@ -71,4 +74,17 @@ default <B> Monad<B, M> discardL(Applicative<B, M> appB) {
7174
default <B> Monad<A, M> discardR(Applicative<B, M> appB) {
7275
return Applicative.super.discardR(appB).coerce();
7376
}
77+
78+
/**
79+
* Convenience static method equivalent to {@link Monad#flatMap(Function) flatMap}{@link Id#id() (id())};
80+
*
81+
* @param mma the outer monad
82+
* @param <M> the monad type
83+
* @param <A> the nested type parameter
84+
* @param <MA> the nested monad
85+
* @return the nested monad
86+
*/
87+
static <M extends Monad, A, MA extends Monad<A, M>> MA join(Monad<? extends MA, M> mma) {
88+
return mma.flatMap(id()).coerce();
89+
}
7490
}

src/test/java/testsupport/traits/MonadLaws.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import static com.jnape.palatable.lambda.adt.Maybe.nothing;
1313
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
1414
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
15+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Upcast.upcast;
16+
import static com.jnape.palatable.lambda.monad.Monad.join;
1517
import static java.util.Arrays.asList;
1618

1719
public class MonadLaws<M extends Monad> implements Trait<Monad<?, M>> {
@@ -22,7 +24,8 @@ public void test(Monad<?, M> m) {
2224
.<Function<Monad<?, M>, Maybe<String>>>foldMap(f -> f.apply(m), asList(
2325
this::testLeftIdentity,
2426
this::testRightIdentity,
25-
this::testAssociativity))
27+
this::testAssociativity,
28+
this::testJoin))
2629
.peek(s -> {
2730
throw new AssertionError("The following Monad laws did not hold for instance of " + m.getClass() + ": \n\t - " + s);
2831
});
@@ -32,21 +35,29 @@ private Maybe<String> testLeftIdentity(Monad<?, M> m) {
3235
Object a = new Object();
3336
Fn1<Object, Monad<Object, M>> fn = id().andThen(m::pure);
3437
return m.pure(a).flatMap(fn).equals(fn.apply(a))
35-
? nothing()
36-
: just("left identity (m.pure(a).flatMap(fn).equals(fn.apply(a)))");
38+
? nothing()
39+
: just("left identity (m.pure(a).flatMap(fn).equals(fn.apply(a)))");
3740
}
3841

3942
private Maybe<String> testRightIdentity(Monad<?, M> m) {
4043
return m.flatMap(m::pure).equals(m)
41-
? nothing()
42-
: just("right identity: (m.flatMap(m::pure).equals(m))");
44+
? nothing()
45+
: just("right identity: (m.flatMap(m::pure).equals(m))");
4346
}
4447

4548
private Maybe<String> testAssociativity(Monad<?, M> m) {
4649
Fn1<Object, Monad<Object, M>> f = constantly(m.pure(new Object()));
4750
Function<Object, Monad<Object, M>> g = constantly(m.pure(new Object()));
4851
return m.flatMap(f).flatMap(g).equals(m.flatMap(a -> f.apply(a).flatMap(g)))
49-
? nothing()
50-
: just("associativity: (m.flatMap(f).flatMap(g).equals(m.flatMap(a -> f.apply(a).flatMap(g))))");
52+
? nothing()
53+
: just("associativity: (m.flatMap(f).flatMap(g).equals(m.flatMap(a -> f.apply(a).flatMap(g))))");
54+
}
55+
56+
private Maybe<String> testJoin(Monad<?, M> m) {
57+
Monad<Monad<Object, M>, M> mma = m.pure(m.fmap(upcast()));
58+
boolean equals = mma.flatMap(id()).equals(join(mma));
59+
return equals
60+
? nothing()
61+
: just("join: (m.pure(m).flatMap(id())).equals(Monad.join(m.pure(m)))");
5162
}
5263
}

0 commit comments

Comments
 (0)