Skip to content

Commit d6ac5bb

Browse files
committed
Maybe is now a coproduct of Unit and A
1 parent 774d5b0 commit d6ac5bb

File tree

3 files changed

+87
-48
lines changed

3 files changed

+87
-48
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
1515
- monoid folding now implicitly starts with the identity, regardless of iterable population
1616
- `Concat` monoid can now fold infinite iterables
1717
- all `Function<? super XXX, Boolean>` are now `Function<? super XXX, ? extends Boolean>` for better compatibility
18-
- `Either#diverge` returns a `Choice3`
18+
- `Either#diverge` returns a `Choice3`
19+
- `Maybe` is now a `CoProduct2` of `Unit` and `A`
1920

2021
### Added
2122
- `Predicate#predicate` static factory method

src/main/java/com/jnape/palatable/lambda/adt/Maybe.java

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package com.jnape.palatable.lambda.adt;
22

3+
import com.jnape.palatable.lambda.adt.choice.Choice2;
4+
import com.jnape.palatable.lambda.adt.choice.Choice3;
5+
import com.jnape.palatable.lambda.adt.coproduct.CoProduct2;
6+
import com.jnape.palatable.lambda.adt.hlist.HList;
7+
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
38
import com.jnape.palatable.lambda.functions.builtin.fn2.Peek;
49
import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier;
510
import com.jnape.palatable.lambda.functor.Applicative;
@@ -14,6 +19,9 @@
1419
import java.util.function.Supplier;
1520

1621
import static com.jnape.palatable.lambda.adt.Either.left;
22+
import static com.jnape.palatable.lambda.adt.Unit.UNIT;
23+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
24+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
1725

1826
/**
1927
* The optional type, representing a potentially absent value. This is lambda's analog of {@link Optional}, supporting
@@ -22,7 +30,8 @@
2230
* @param <A> the optional parameter type
2331
* @see Optional
2432
*/
25-
public abstract class Maybe<A> implements Monad<A, Maybe>, Traversable<A, Maybe> {
33+
public abstract class Maybe<A> implements CoProduct2<Unit, A, Maybe<A>>, Monad<A, Maybe>, Traversable<A, Maybe> {
34+
2635
private Maybe() {
2736
}
2837

@@ -32,7 +41,9 @@ private Maybe() {
3241
* @param otherSupplier the supplier for the other value
3342
* @return this value, or the supplied other value
3443
*/
35-
public abstract A orElseGet(Supplier<A> otherSupplier);
44+
public final A orElseGet(Supplier<A> otherSupplier) {
45+
return match(__ -> otherSupplier.get(), id());
46+
}
3647

3748
/**
3849
* If the value is present, return it; otherwise, return <code>other</code>.
@@ -130,7 +141,24 @@ public final <B> Maybe<A> discardR(Applicative<B, Maybe> appB) {
130141
}
131142

132143
@Override
133-
public abstract <B> Maybe<B> flatMap(Function<? super A, ? extends Monad<B, Maybe>> f);
144+
public final <B> Maybe<B> flatMap(Function<? super A, ? extends Monad<B, Maybe>> f) {
145+
return match(constantly(nothing()), f.andThen(Applicative::coerce));
146+
}
147+
148+
@Override
149+
public <B> Choice3<Unit, A, B> diverge() {
150+
return match(Choice3::a, Choice3::b);
151+
}
152+
153+
@Override
154+
public Tuple2<Maybe<Unit>, Maybe<A>> project() {
155+
return CoProduct2.super.project().into(HList::tuple);
156+
}
157+
158+
@Override
159+
public Choice2<A, Unit> invert() {
160+
return match(Choice2::b, Choice2::a);
161+
}
134162

135163
/**
136164
* If this value is present, accept it by <code>consumer</code>; otherwise, do nothing.
@@ -142,6 +170,14 @@ public final Maybe<A> peek(Consumer<A> consumer) {
142170
return Peek.peek(consumer, this);
143171
}
144172

173+
@Override
174+
@SuppressWarnings("unchecked")
175+
public final <B, App extends Applicative, TravB extends Traversable<B, Maybe>, AppB extends Applicative<B, App>, AppTrav extends Applicative<TravB, App>> AppTrav traverse(
176+
Function<? super A, ? extends AppB> fn,
177+
Function<? super TravB, ? extends AppTrav> pure) {
178+
return match(__ -> pure.apply((TravB) Maybe.<B>nothing()), a -> (AppTrav) fn.apply(a).fmap(Maybe::just));
179+
}
180+
145181
/**
146182
* Convenience static factory method for creating a {@link Maybe} from an {@link Either}. If <code>either</code> is
147183
* a right value, wrap the value in a <code>just</code> and return it; otherwise, return {@link #nothing()}.
@@ -192,80 +228,60 @@ public static <A> Maybe<A> just(A a) {
192228
return new Just<>(a);
193229
}
194230

231+
/**
232+
* Return nothing.
233+
*
234+
* @param <A> the type of the value, if there was one
235+
* @return nothing
236+
*/
195237
@SuppressWarnings("unchecked")
196238
public static <A> Maybe<A> nothing() {
197239
return Nothing.INSTANCE;
198240
}
199241

200-
private static final class Just<A> extends Maybe<A> {
201-
202-
private final A a;
203-
204-
private Just(A a) {
205-
this.a = a;
206-
}
207-
208-
@Override
209-
public A orElseGet(Supplier<A> otherSupplier) {
210-
return a;
211-
}
212-
213-
@Override
214-
public <B> Maybe<B> flatMap(Function<? super A, ? extends Monad<B, Maybe>> f) {
215-
return f.apply(a).coerce();
216-
}
217-
218-
@Override
219-
@SuppressWarnings("unchecked")
220-
public <B, App extends Applicative, TravB extends Traversable<B, Maybe>,
221-
AppB extends Applicative<B, App>, AppTrav extends Applicative<TravB, App>> AppTrav traverse(
222-
Function<? super A, ? extends AppB> fn, Function<? super TravB, ? extends AppTrav> pure) {
223-
return fn.apply(a).fmap(Just::new).<TravB>fmap(Applicative::coerce).coerce();
224-
}
242+
private static final class Nothing<A> extends Maybe<A> {
243+
private static final Nothing INSTANCE = new Nothing();
225244

226-
@Override
227-
public boolean equals(Object other) {
228-
return other instanceof Just && Objects.equals(this.a, ((Just) other).a);
245+
private Nothing() {
229246
}
230247

231248
@Override
232-
public int hashCode() {
233-
return Objects.hash(a);
249+
public <R> R match(Function<? super Unit, ? extends R> aFn, Function<? super A, ? extends R> bFn) {
250+
return aFn.apply(UNIT);
234251
}
235252

236253
@Override
237254
public String toString() {
238-
return "Just " + a;
255+
return "Nothing";
239256
}
240257
}
241258

242-
private static final class Nothing<A> extends Maybe<A> {
243-
private static final Nothing INSTANCE = new Nothing();
259+
private static final class Just<A> extends Maybe<A> {
244260

245-
private Nothing() {
261+
private final A a;
262+
263+
private Just(A a) {
264+
this.a = a;
246265
}
247266

248267
@Override
249-
@SuppressWarnings("unchecked")
250-
public <B> Maybe<B> flatMap(Function<? super A, ? extends Monad<B, Maybe>> f) {
251-
return nothing();
268+
public <R> R match(Function<? super Unit, ? extends R> aFn, Function<? super A, ? extends R> bFn) {
269+
return bFn.apply(a);
252270
}
253271

254272
@Override
255-
@SuppressWarnings("unchecked")
256-
public <B, App extends Applicative, TravB extends Traversable<B, Maybe>, AppB extends Applicative<B, App>, AppTrav extends Applicative<TravB, App>> AppTrav traverse(
257-
Function<? super A, ? extends AppB> fn, Function<? super TravB, ? extends AppTrav> pure) {
258-
return pure.apply((TravB) nothing());
273+
public boolean equals(Object other) {
274+
return other instanceof Just && Objects.equals(this.a, ((Just) other).a);
259275
}
260276

261277
@Override
262-
public A orElseGet(Supplier<A> otherSupplier) {
263-
return otherSupplier.get();
278+
public int hashCode() {
279+
return Objects.hash(a);
264280
}
265281

266282
@Override
267283
public String toString() {
268-
return "Nothing";
284+
return "Just " + a;
269285
}
270286
}
271287
}

src/test/java/com/jnape/palatable/lambda/adt/MaybeTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.jnape.palatable.lambda.adt;
22

3+
import com.jnape.palatable.lambda.adt.choice.Choice2;
4+
import com.jnape.palatable.lambda.adt.choice.Choice3;
35
import com.jnape.palatable.traitor.annotations.TestTraits;
46
import com.jnape.palatable.traitor.framework.Subjects;
57
import com.jnape.palatable.traitor.runners.Traits;
@@ -17,6 +19,8 @@
1719
import static com.jnape.palatable.lambda.adt.Either.right;
1820
import static com.jnape.palatable.lambda.adt.Maybe.just;
1921
import static com.jnape.palatable.lambda.adt.Maybe.nothing;
22+
import static com.jnape.palatable.lambda.adt.Unit.UNIT;
23+
import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
2024
import static com.jnape.palatable.lambda.functions.builtin.fn2.Eq.eq;
2125
import static com.jnape.palatable.traitor.framework.Subjects.subjects;
2226
import static org.junit.Assert.assertEquals;
@@ -108,4 +112,22 @@ public void justOrThrow() {
108112
public void nothingOrThrow() {
109113
nothing().orElseThrow(IllegalStateException::new);
110114
}
115+
116+
@Test
117+
public void divergesIntoChoice3() {
118+
assertEquals(Choice3.a(UNIT), nothing().diverge());
119+
assertEquals(Choice3.b(1), just(1).diverge());
120+
}
121+
122+
@Test
123+
public void projectsIntoTuple2() {
124+
assertEquals(tuple(just(UNIT), nothing()), nothing().project());
125+
assertEquals(tuple(nothing(), just(1)), just(1).project());
126+
}
127+
128+
@Test
129+
public void invertsIntoChoice2() {
130+
assertEquals(Choice2.b(UNIT), nothing().invert());
131+
assertEquals(Choice2.a(1), just(1).invert());
132+
}
111133
}

0 commit comments

Comments
 (0)