Skip to content

Commit a593cf3

Browse files
committed
Maybe arrives; alas poor LambdaOptional, we barely knew him.
1 parent 0f4bf36 commit a593cf3

11 files changed

Lines changed: 448 additions & 170 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
1212
- `Snoc`, for lazily appending an element to the end of an `Iterable`
1313
- `Coalesce`, for folding an `Iterable<Either<L, R>>` into an `Either<Iterable<L>, Iterable<R>>`
1414
- `And`, `Or`, and `Xor` all gain `BiPredicate<Boolean, Boolean>` properties
15-
- `LambdaOptional` and `LambdaIterable`, adapters for `Optional` and `Iterable` that support lambda types
16-
- `Lambda`, providing static factory methods for `LambdaOptional` and `LambdaIterable`
15+
- `LambdaIterable`, an adapter `Iterable` that support lambda types
16+
- `Maybe`, lambda's analog of `java.util.Optional` conforming to all the lambda types
1717
- `Contravariant`, an interface representing functors that map contravariantly over their parameters
1818
- `Profunctor` extends `Contravariant`
1919

@@ -23,8 +23,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
2323
- `Fn2#adapt(BiFunction<A, B, C> biFunction)`, deprecated in previous release
2424

2525
### Deprecated
26-
- `Traversables` and all methods therein, in favor of new `Lambda` methods
27-
- `TraversableOptional` in favor of `LambdaOptional`
26+
- `Traversables` and all methods therein, in favor of either `LambdaIterable` or `Maybe`
27+
- `TraversableOptional` in favor of `Maybe`
2828
- `TraversableIterable` in favor of `LambdaIterable`
2929

3030
### Changed
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
package com.jnape.palatable.lambda.adt;
2+
3+
import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier;
4+
import com.jnape.palatable.lambda.functor.Applicative;
5+
import com.jnape.palatable.lambda.functor.Functor;
6+
import com.jnape.palatable.lambda.monad.Monad;
7+
import com.jnape.palatable.lambda.traversable.Traversable;
8+
9+
import java.util.Objects;
10+
import java.util.Optional;
11+
import java.util.function.Consumer;
12+
import java.util.function.Function;
13+
import java.util.function.Supplier;
14+
15+
import static com.jnape.palatable.lambda.adt.Either.left;
16+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
17+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
18+
19+
/**
20+
* The optional type, representing a potentially absent value. This is lambda's analog of {@link Optional}, supporting
21+
* all the usual suspects like {@link Functor}, {@link Applicative}, {@link Traversable}, etc.
22+
*
23+
* @param <A> the optional parameter type
24+
* @see Optional
25+
*/
26+
public abstract class Maybe<A> implements Monad<A, Maybe>, Traversable<A, Maybe> {
27+
private Maybe() {
28+
}
29+
30+
/**
31+
* If the value is present, return it; otherwise, return the value supplied by <code>otherSupplier</code>.
32+
*
33+
* @param otherSupplier the supplier for the other value
34+
* @return this value, or the supplied other value
35+
*/
36+
public abstract A orElseGet(Supplier<A> otherSupplier);
37+
38+
/**
39+
* If the value is present, return it; otherwise, return <code>other</code>.
40+
*
41+
* @param other the other value
42+
* @return this value, or the other value
43+
*/
44+
public final A orElse(A other) {
45+
return orElseGet(() -> other);
46+
}
47+
48+
/**
49+
* If the value is present, return it; otherwise, throw the {@link Throwable} supplied by
50+
* <code>throwableSupplier</code>.
51+
*
52+
* @param throwableSupplier the supplier of the potentially thrown {@link Throwable}
53+
* @param <E> the Throwable type
54+
* @return the value, if present
55+
* @throws E the throwable, if the value is absent
56+
*/
57+
public final <E extends Throwable> A orThrow(Supplier<E> throwableSupplier) throws E {
58+
return orElseGet((CheckedSupplier<E, A>) () -> {
59+
throw throwableSupplier.get();
60+
});
61+
}
62+
63+
/**
64+
* If this value is present and satisfies <code>predicate</code>, return <code>just</code> the value; otherwise,
65+
* return <code>nothing</code>.
66+
*
67+
* @param predicate the predicate to apply to the possibly absent value
68+
* @return maybe the present value that satisfied the predicate
69+
*/
70+
public final Maybe<A> filter(Function<? super A, ? extends Boolean> predicate) {
71+
return flatMap(a -> predicate.apply(a) ? just(a) : nothing());
72+
}
73+
74+
/**
75+
* If this value is absent, return the value supplied by <code>lSupplier</code> wrapped in <code>Either.left</code>.
76+
* Otherwise, wrap the value in <code>Either.right</code> and return it.
77+
*
78+
* @param lSupplier the supplier for the left value
79+
* @param <L> the left parameter type
80+
* @return this value wrapped in an Either.right, or an Either.left around the result of lSupplier
81+
*/
82+
public final <L> Either<L, A> toEither(Supplier<L> lSupplier) {
83+
return fmap(Either::<L, A>right).orElseGet(() -> left(lSupplier.get()));
84+
}
85+
86+
/**
87+
* Convert to {@link Optional}.
88+
*
89+
* @return the Optional
90+
*/
91+
public final Optional<A> toOptional() {
92+
return fmap(Optional::of).orElseGet(Optional::empty);
93+
}
94+
95+
/**
96+
* Lift the value into the {@link Maybe} monad
97+
*
98+
* @param b the value
99+
* @param <B> the value type
100+
* @return Just b
101+
*/
102+
@Override
103+
public final <B> Maybe<B> pure(B b) {
104+
return just(b);
105+
}
106+
107+
/**
108+
* {@inheritDoc}
109+
* <p>
110+
* If the value is present, return {@link Maybe#just} <code>fn</code> applied to the value; otherwise, return
111+
* {@link Maybe#nothing}.
112+
*/
113+
@Override
114+
public final <B> Maybe<B> fmap(Function<? super A, ? extends B> fn) {
115+
return Monad.super.<B>fmap(fn).coerce();
116+
}
117+
118+
@Override
119+
public final <B> Maybe<B> zip(Applicative<Function<? super A, ? extends B>, Maybe> appFn) {
120+
return Monad.super.zip(appFn).coerce();
121+
}
122+
123+
@Override
124+
public final <B> Maybe<B> discardL(Applicative<B, Maybe> appB) {
125+
return Monad.super.discardL(appB).coerce();
126+
}
127+
128+
@Override
129+
public final <B> Maybe<A> discardR(Applicative<B, Maybe> appB) {
130+
return Monad.super.discardR(appB).coerce();
131+
}
132+
133+
@Override
134+
public abstract <B> Maybe<B> flatMap(Function<? super A, ? extends Monad<B, Maybe>> f);
135+
136+
@Override
137+
public abstract <B, App extends Applicative> Applicative<Maybe<B>, App> traverse(
138+
Function<? super A, ? extends Applicative<B, App>> fn,
139+
Function<? super Traversable<B, Maybe>, ? extends Applicative<? extends Traversable<B, Maybe>, App>> pure);
140+
141+
/**
142+
* If this value is present, accept it by <code>consumer</code>; otherwise, do nothing.
143+
*
144+
* @param consumer the consumer
145+
* @return the same Maybe instance
146+
*/
147+
public final Maybe<A> peek(Consumer<A> consumer) {
148+
return fmap(a -> {
149+
consumer.accept(a);
150+
return a;
151+
});
152+
}
153+
154+
/**
155+
* Convenience static factory method for creating a {@link Maybe} from an {@link Either}. If <code>either</code> is
156+
* a right value, wrap the value in a <code>just</code> and return it; otherwise, return {@link #nothing()}.
157+
*
158+
* @param either the either instance
159+
* @param <A> the potential right value
160+
* @return "Just" the right value, or nothing
161+
*/
162+
public static <A> Maybe<A> fromEither(Either<?, A> either) {
163+
return either.match(constantly(nothing()), Maybe::just);
164+
}
165+
166+
/**
167+
* Convenience static factory method for creating a {@link Maybe} from an {@link Optional}.
168+
*
169+
* @param optional the optional
170+
* @param <A> the optional parameter type
171+
* @return the equivalent Maybe instance
172+
*/
173+
public static <A> Maybe<A> fromOptional(Optional<? extends A> optional) {
174+
return optional.<A>map(id()).map(Maybe::just).orElse(Maybe.nothing());
175+
}
176+
177+
/**
178+
* Lift a potentially null value into {@link Maybe}. If <code>a</code> is not null, returns <code>just(a)</code>;
179+
* otherwise, returns {@link #nothing()}.
180+
*
181+
* @param a the potentially null value
182+
* @param <A> the value parameter type
183+
* @return "Just" the value, or nothing
184+
*/
185+
public static <A> Maybe<A> maybe(A a) {
186+
return a == null ? nothing() : just(a);
187+
}
188+
189+
/**
190+
* Lift a non-null value into {@link Maybe}. This differs from {@link Maybe#maybe} in that the value *must* be
191+
* non-null; if it is null, a {@link NullPointerException} is thrown.
192+
*
193+
* @param a the non-null value
194+
* @param <A> the value parameter type
195+
* @return "Just" the value
196+
* @throws NullPointerException if a is null
197+
*/
198+
public static <A> Maybe<A> just(A a) {
199+
if (a == null)
200+
throw new NullPointerException();
201+
return new Just<>(a);
202+
}
203+
204+
@SuppressWarnings("unchecked")
205+
public static <A> Maybe<A> nothing() {
206+
return Nothing.INSTANCE;
207+
}
208+
209+
private static final class Just<A> extends Maybe<A> {
210+
211+
private final A a;
212+
213+
private Just(A a) {
214+
this.a = a;
215+
}
216+
217+
@Override
218+
public A orElseGet(Supplier<A> otherSupplier) {
219+
return a;
220+
}
221+
222+
@Override
223+
public <B> Maybe<B> flatMap(Function<? super A, ? extends Monad<B, Maybe>> f) {
224+
return f.apply(a).coerce();
225+
}
226+
227+
@Override
228+
public <B, App extends Applicative> Applicative<Maybe<B>, App> traverse(
229+
Function<? super A, ? extends Applicative<B, App>> fn,
230+
Function<? super Traversable<B, Maybe>, ? extends Applicative<? extends Traversable<B, Maybe>, App>> pure) {
231+
return fn.apply(a).fmap(Just::new);
232+
}
233+
234+
@Override
235+
public boolean equals(Object other) {
236+
return other instanceof Just && Objects.equals(this.a, ((Just) other).a);
237+
}
238+
239+
@Override
240+
public int hashCode() {
241+
return Objects.hash(a);
242+
}
243+
244+
@Override
245+
public String toString() {
246+
return "Just{" +
247+
"a=" + a +
248+
'}';
249+
}
250+
}
251+
252+
private static final class Nothing<A> extends Maybe<A> {
253+
private static final Nothing INSTANCE = new Nothing();
254+
255+
private Nothing() {
256+
}
257+
258+
@Override
259+
@SuppressWarnings("unchecked")
260+
public <B> Maybe<B> flatMap(Function<? super A, ? extends Monad<B, Maybe>> f) {
261+
return nothing();
262+
}
263+
264+
@Override
265+
@SuppressWarnings("unchecked")
266+
public <B, App extends Applicative> Applicative<Maybe<B>, App> traverse(
267+
Function<? super A, ? extends Applicative<B, App>> fn,
268+
Function<? super Traversable<B, Maybe>, ? extends Applicative<? extends Traversable<B, Maybe>, App>> pure) {
269+
return (Applicative<Maybe<B>, App>) pure.apply(nothing());
270+
}
271+
272+
@Override
273+
public A orElseGet(Supplier<A> otherSupplier) {
274+
return otherSupplier.get();
275+
}
276+
277+
@Override
278+
public String toString() {
279+
return "Nothing{}";
280+
}
281+
}
282+
}

src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Sequence.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.jnape.palatable.lambda.functions.builtin.fn2;
22

3+
import com.jnape.palatable.lambda.adt.Maybe;
34
import com.jnape.palatable.lambda.functions.Fn1;
45
import com.jnape.palatable.lambda.functions.Fn2;
56
import com.jnape.palatable.lambda.functor.Applicative;
67
import com.jnape.palatable.lambda.traversable.LambdaIterable;
7-
import com.jnape.palatable.lambda.traversable.LambdaOptional;
88
import com.jnape.palatable.lambda.traversable.Traversable;
99

1010
import java.util.Optional;
@@ -77,9 +77,9 @@ TravApp extends Traversable<? extends Applicative<A, App>, Trav>> AppTrav sequen
7777
@SuppressWarnings("unchecked")
7878
public static <A, App extends Applicative, AppOptional extends Applicative<Optional<A>, App>, OptionalApp extends Optional<? extends Applicative<A, App>>> Fn1<Function<? super Optional<A>, ? extends AppOptional>, AppOptional> sequence(
7979
OptionalApp optionalApp) {
80-
return pure -> (AppOptional) sequence(LambdaOptional.wrap(optionalApp), x -> pure.apply(((LambdaOptional<A>) x).unwrap())
81-
.fmap(LambdaOptional::wrap))
82-
.fmap(LambdaOptional::unwrap);
80+
return pure -> (AppOptional) sequence(Maybe.fromOptional(optionalApp), x -> pure.apply(((Maybe<A>) x).toOptional())
81+
.fmap(Maybe::fromOptional))
82+
.fmap(Maybe::toOptional);
8383
}
8484

8585
public static <A, App extends Applicative, AppIterable extends Applicative<Iterable<A>, App>,

src/main/java/com/jnape/palatable/lambda/functor/Profunctor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public interface Profunctor<A, B, PF extends Profunctor> extends Contravariant<A
2626

2727
/**
2828
* Dually map contravariantly over the left parameter and covariantly over the right parameter. This is isomorphic
29-
* to <code>diMapL(lFn).diMapR(rFn)</code>.¬
29+
* to <code>diMapL(lFn).diMapR(rFn)</code>.
3030
*
3131
* @param <Z> the new left parameter type
3232
* @param <C> the new right parameter type
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.jnape.palatable.lambda.lens.lenses;
2+
3+
import com.jnape.palatable.lambda.adt.Maybe;
4+
import com.jnape.palatable.lambda.lens.Lens;
5+
6+
import static com.jnape.palatable.lambda.lens.Lens.simpleLens;
7+
import static com.jnape.palatable.lambda.lens.functions.Set.set;
8+
import static com.jnape.palatable.lambda.lens.functions.View.view;
9+
10+
/**
11+
* Lenses for {@link Maybe}.
12+
*/
13+
public final class MaybeLens {
14+
private MaybeLens() {
15+
}
16+
17+
public static <S, T, A, B> Lens<Maybe<S>, T, A, B> liftS(Lens<S, T, A, B> lens, S defaultValue) {
18+
return lens.mapS(m -> m.orElse(defaultValue));
19+
}
20+
21+
public static <S, T, A, B> Lens<S, Maybe<T>, A, B> liftT(Lens<S, T, A, B> lens) {
22+
return lens.mapT(Maybe::just);
23+
}
24+
25+
public static <S, T, A, B> Lens<S, T, Maybe<A>, B> liftA(Lens<S, T, A, B> lens) {
26+
return lens.mapA(Maybe::just);
27+
}
28+
29+
public static <S, T, A, B> Lens<S, T, A, Maybe<B>> liftB(Lens<S, T, A, B> lens, B defaultValue) {
30+
return lens.mapB(m -> m.orElse(defaultValue));
31+
}
32+
33+
public static <S, A> Lens.Simple<Maybe<S>, Maybe<A>> asMaybe(Lens<S, S, A, A> lens) {
34+
return simpleLens(m -> m.fmap(view(lens)),
35+
(maybeS, maybeB) -> maybeS.flatMap(s -> maybeB.fmap(a -> {
36+
return set(lens, a, s);
37+
})));
38+
}
39+
}

0 commit comments

Comments
 (0)