diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1a1bf3b6b..314f61106 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,20 +1,36 @@
# Change Log
+
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/).
## [Unreleased]
+### Changed
+- `Absent` folds short-circuit on the first `nothing()`
+- `EitherMatcher#isLeftThat/isRightThat` support contravariant bounds on their delegates
+
+### Added
+- `IterateT#runStep`, a method used to run a single step of an IterateT without the contractual guarantee of emitting a
+ value or reaching the end
+- `These#fromMaybes :: Maybe a -> Maybe b -> Maybe (These a b)`
+- `EitherMatcher#isLeftOf/isRightOf` for asserting equality
+
+### Fixed
+- `WriterT` now keeps an immediate reference to the embedded monad's `pure`
+
+## [5.3.0] - 2020-12-07
+
### Changed
- `IterateT#unfold` now only computes a single `Pure` for the given input
- `ReaderT#fmap` and `StateT#fmap` avoid unnecessary calls to `pure`
- `MaybeT` implements `MonadError`
-- `Tuple2-8#init`, for populating a `TupleN` with all but the last element
### Added
- `$`, function application represented as a higher-order `Fn2`
-- `Fn1#withSelf`, a static method for constructing a self-referencing `Fn1`
+- `Fn1#withSelf`, a static method for constructing a self-referencing `Fn1`
- `HNil/SingletonHList/TupleX#snoc`, a method to add a new last element (append to a tuple)
+- `Tuple2-8#init`, for populating a `TupleN` with all but the last element
### Fixed
- `IterateT#trampolineM` now yields and stages all recursive result values, rather
@@ -576,7 +592,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
- `Monadic/Dyadic/TriadicFunction`, `Predicate`, `Tuple2`, `Tuple3`
- `Functor`, `BiFunctor`, `ProFunctor`
-[Unreleased]: https://github.com/palatable/lambda/compare/lambda-5.2.0...HEAD
+[Unreleased]: https://github.com/palatable/lambda/compare/lambda-5.3.0...HEAD
+[5.3.0]: https://github.com/palatable/lambda/compare/lambda-5.2.0...lambda-5.3.0
[5.2.0]: https://github.com/palatable/lambda/compare/lambda-5.1.0...lambda-5.2.0
[5.1.0]: https://github.com/palatable/lambda/compare/lambda-5.0.0...lambda-5.1.0
[5.0.0]: https://github.com/palatable/lambda/compare/lambda-4.0.0...lambda-5.0.0
diff --git a/README.md b/README.md
index eee42045c..060d4e9ee 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ Functional patterns for Java
- [Either](#either)
- [Lenses](#lenses)
- [Notes](#notes)
- - [Community](#community)
+ - [Ecosystem](#ecosystem)
- [License](#license)
Background
@@ -61,14 +61,14 @@ Add the following dependency to your:
com.jnape.palatable
lambda
- 5.2.0
+ 5.3.0
```
`build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)):
```gradle
-compile group: 'com.jnape.palatable', name: 'lambda', version: '5.2.0'
+compile group: 'com.jnape.palatable', name: 'lambda', version: '5.3.0'
```
Examples
@@ -740,9 +740,18 @@ Wherever possible, _lambda_ maintains interface compatibility with similar, fami
Unfortunately, due to Java's type hierarchy and inheritance inconsistencies, this is not always possible. One surprising example of this is how `Fn1` extends `j.u.f.Function`, but `Fn2` does not extend `j.u.f.BiFunction`. This is because `j.u.f.BiFunction` itself does not extend `j.u.f.Function`, but it does define methods that collide with `j.u.f.Function`. For this reason, both `Fn1` and `Fn2` cannot extend their Java counterparts without sacrificing their own inheritance hierarchy. These types of asymmetries are, unfortunately, not uncommon; however, wherever these situations arise, measures are taken to attempt to ease the transition in and out of core Java types (in the case of `Fn2`, a supplemental `#toBiFunction` method is added). I do not take these inconveniences for granted, and I'm regularly looking for ways to minimize the negative impact of this as much as possible. Suggestions and use cases that highlight particular pain points here are particularly appreciated.
-Community
+Ecosystem
-----
-There are some open-sourced community projects that depend on _lambda_ for their own functionality: these projects are listed below (note that these projects are _not_ affiliated with lambda, and have their own maintainers). If you use _lambda_ in your own open-sourced project, feel free to create an issue and I'll be happy to review the project and add it to this section!
+
+### Official extension libraries:
+
+These are officially supported libraries that extend lambda's core functionality and are developed under the same governance and processes as lambda.
+
+- [Shōki](https://github.com/palatable/shoki) - Purely functional, persistent data structures for the JVM
+
+### Third-party community libraries:
+
+These are open-sourced community projects that rely on _lambda_ for significant functionality, but are not necessarily affiliated with lambda and have their own separate maintainers. If you use _lambda_ in your own open-sourced project, feel free to create an issue and I'll be happy to review the project and add it to this section!
- [Enhanced Iterables](https://github.com/kschuetz/enhanced-iterables) - Kevin Schuetz [@kschuetz](https://github.com/kschuetz)
- [Collection Views](https://github.com/kschuetz/collection-views) - Kevin Schuetz [@kschuetz](https://github.com/kschuetz)
diff --git a/pom.xml b/pom.xml
index 820393917..231ca6768 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
lambda
- 5.3.0
+ 5.4.0
jar
Lambda
diff --git a/src/main/java/com/jnape/palatable/lambda/adt/These.java b/src/main/java/com/jnape/palatable/lambda/adt/These.java
index 6721d33e8..4fc4cc27b 100644
--- a/src/main/java/com/jnape/palatable/lambda/adt/These.java
+++ b/src/main/java/com/jnape/palatable/lambda/adt/These.java
@@ -189,6 +189,25 @@ public static These both(A a, B b) {
return new Both<>(tuple(a, b));
}
+ /**
+ * Convenience method for converting a pair of {@link Maybe}s into a {@link Maybe} of {@link These}. If both
+ * {@link Maybe}s are {@link Maybe#just} then the result is a {@link Maybe#just} {@link These#both}. If only one
+ * {@link Maybe} is {@link Maybe#just} then it will be {@link Maybe#just} {@link These#a} or
+ * {@link Maybe#just} {@link These#b}. If both {@link Maybe}s are {@link Maybe#nothing} then the result will be
+ * {@link Maybe#nothing}.
+ *
+ * @param maybeA the first optional value
+ * @param maybeB the second optional value
+ * @param the first possible type
+ * @param the second possible type
+ * @return the wrapped values as a {@link Maybe}<{@link These}<A,B>>
+ */
+ public static Maybe> fromMaybes(Maybe maybeA, Maybe maybeB) {
+ return maybeA.fmap(a -> maybeB.fmap(b -> both(a, b)).orElse(a(a)))
+ .fmap(Maybe::just)
+ .orElse(maybeB.fmap(These::b));
+ }
+
/**
* The canonical {@link Pure} instance for {@link These}.
*
diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java
index d651795b5..cb719060b 100644
--- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java
+++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java
@@ -65,8 +65,7 @@
* @param the effect type
* @param the element type
*/
-public class IterateT, A> implements
- MonadT, IterateT, ?>> {
+public class IterateT, A> implements MonadT, IterateT, ?>> {
private final Pure pureM;
private final ImmutableQueue>>, M>>, MonadRec>> spine;
@@ -84,15 +83,35 @@ private IterateT(Pure pureM,
* @return the embedded {@link Monad}
*/
public >>, M>> MMTA runIterateT() {
- MonadRec>>, M>>, MonadRec>>, M>
- mSpine = pureM.apply(spine);
- return mSpine.trampolineM(tSpine -> tSpine.head().>>, M>>, MonadRec>>, Maybe>>>, M>>match(
- ___ -> pureM.apply(terminate(nothing())),
+ return pureM., MonadRec, M>>apply(this)
+ .>>>trampolineM(iterateT -> iterateT.runStep()
+ .fmap(maybeMore -> maybeMore.match(
+ fn0(() -> terminate(nothing())),
+ t -> t.into((Maybe maybeA, IterateT as) -> maybeA.match(
+ fn0(() -> recurse(as)),
+ a -> terminate(just(tuple(a, as))))))))
+ .coerce();
+ }
+
+ /**
+ * Run a single step of this {@link IterateT}, where a step is the smallest amount of work that could possibly be
+ * productive in advancing through the {@link IterateT}. Useful for implementing interleaving algorithms that
+ * require {@link IterateT IterateTs} to yield, emit, or terminate as soon as possible, regardless of whether the
+ * next element is readily available.
+ *
+ * @param the witnessed target type of the step
+ * @return the step
+ */
+ public , IterateT>>, M>> MStep runStep() {
+ return spine.head().match(
+ fn0(() -> pureM., IterateT>>, MStep>apply(nothing())),
thunkOrReal -> thunkOrReal.match(
- thunk -> thunk.apply().fmap(m -> m.match(
- ___ -> recurse(tSpine.tail()),
- t -> terminate(just(t.fmap(as -> new IterateT<>(pureM, as.spine.concat(tSpine.tail()))))))),
- real -> real.fmap(a -> terminate(just(tuple(a, new IterateT<>(pureM, tSpine.tail())))))))).coerce();
+ thunk -> thunk.apply()., IterateT>>>fmap(m -> m.match(
+ fn0(() -> just(tuple(nothing(), new IterateT<>(pureM, spine.tail())))),
+ t -> just(t.biMap(Maybe::just,
+ as -> new IterateT<>(pureM, as.spine.concat(spine.tail()))))))
+ .coerce(),
+ ma -> ma.fmap(a -> just(tuple(just(a), new IterateT<>(pureM, spine.tail())))).coerce()));
}
/**
diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/WriterT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/WriterT.java
index 7a0205543..c8625bbf8 100644
--- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/WriterT.java
+++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/WriterT.java
@@ -34,9 +34,12 @@ public final class WriterT, A> implements
MonadWriter>,
MonadT, WriterT> {
+ private final Pure pureM;
private final Fn1 super Monoid, ? extends MonadRec, M>> writerFn;
- private WriterT(Fn1 super Monoid, ? extends MonadRec, M>> writerFn) {
+ private WriterT(Pure pureM,
+ Fn1 super Monoid, ? extends MonadRec, M>> writerFn) {
+ this.pureM = pureM;
this.writerFn = writerFn;
}
@@ -82,7 +85,7 @@ public > MW execWriterT(Monoid monoid) {
*/
@Override
public WriterT> listens(Fn1 super W, ? extends B> fn) {
- return new WriterT<>(writerFn.fmap(m -> m.fmap(into((a, w) -> both(both(constantly(a), fn), id(), w)))));
+ return new WriterT<>(pureM, writerFn.fmap(m -> m.fmap(into((a, w) -> both(both(constantly(a), fn), id(), w)))));
}
/**
@@ -90,7 +93,7 @@ public WriterT> listens(Fn1 super W, ? extends B> fn) {
*/
@Override
public WriterT censor(Fn1 super W, ? extends W> fn) {
- return new WriterT<>(writerFn.fmap(mt -> mt.fmap(t -> t.fmap(fn))));
+ return new WriterT<>(pureM, writerFn.fmap(mt -> mt.fmap(t -> t.fmap(fn))));
}
/**
@@ -107,7 +110,7 @@ public > WriterT lift(MonadRec mb) {
@Override
public WriterT trampolineM(
Fn1 super A, ? extends MonadRec, WriterT>> fn) {
- return new WriterT<>(monoid -> runWriterT(monoid).trampolineM(into((a, w) -> fn.apply(a)
+ return new WriterT<>(pureM, monoid -> runWriterT(monoid).trampolineM(into((a, w) -> fn.apply(a)
.>>coerce()
.runWriterT(monoid).fmap(t -> t.fmap(monoid.apply(w)))
.fmap(into((aOrB, w_) -> aOrB.biMap(a_ -> tuple(a_, w_), b -> tuple(b, w_)))))));
@@ -126,7 +129,7 @@ public WriterT fmap(Fn1 super A, ? extends B> fn) {
*/
@Override
public WriterT pure(B b) {
- return new WriterT<>(m -> runWriterT(m).pure(tuple(b, m.identity())));
+ return new WriterT<>(pureM, m -> pureM.apply(tuple(b, m.identity())));
}
/**
@@ -134,7 +137,7 @@ public WriterT pure(B b) {
*/
@Override
public WriterT flatMap(Fn1 super A, ? extends Monad>> f) {
- return new WriterT<>(monoid -> writerFn.apply(monoid)
+ return new WriterT<>(pureM, monoid -> writerFn.apply(monoid)
.flatMap(into((a, w) -> f.apply(a).>coerce().runWriterT(monoid)
.fmap(t -> t.fmap(monoid.apply(w))))));
}
@@ -144,7 +147,7 @@ public WriterT flatMap(Fn1 super A, ? extends Monad WriterT zip(Applicative, WriterT> appFn) {
- return new WriterT<>(monoid -> runWriterT(monoid)
+ return new WriterT<>(pureM, monoid -> runWriterT(monoid)
.zip(appFn.>>coerce().runWriterT(monoid)
.fmap(into((f, y) -> into((a, x) -> tuple(f.apply(a), monoid.apply(x, y)))))));
}
@@ -196,7 +199,7 @@ public static > WriterT tell(MonadRec, A> WriterT listen(MonadRec ma) {
- return new WriterT<>(monoid -> ma.fmap(a -> tuple(a, monoid.identity())));
+ return new WriterT<>(Pure.of(ma), monoid -> ma.fmap(a -> tuple(a, monoid.identity())));
}
/**
@@ -209,7 +212,7 @@ public static , A> WriterT listen(MonadRec<
* @return the {@link WriterT}
*/
public static , A> WriterT writerT(MonadRec, M> maw) {
- return new WriterT<>(constantly(maw));
+ return new WriterT<>(Pure.of(maw), constantly(maw));
}
/**
diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java
index a692eac77..68162ad74 100644
--- a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java
+++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java
@@ -2,11 +2,21 @@
import com.jnape.palatable.lambda.adt.Maybe;
import com.jnape.palatable.lambda.functions.Fn1;
-import com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2;
+import com.jnape.palatable.lambda.functions.builtin.fn3.FoldRight;
import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory;
+import com.jnape.palatable.lambda.functor.builtin.Lazy;
import com.jnape.palatable.lambda.monoid.builtin.Present;
import com.jnape.palatable.lambda.semigroup.Semigroup;
+import static com.jnape.palatable.lambda.adt.Maybe.nothing;
+import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
+import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
+import static com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2.liftA2;
+import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.recurse;
+import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate;
+import static com.jnape.palatable.lambda.functions.recursion.Trampoline.trampoline;
+import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy;
+
/**
* A {@link Semigroup} instance formed by {@link Maybe}<A> and a semigroup over A. The
* application to two {@link Maybe} values is absence-biased, such that for a given {@link Maybe} x
@@ -32,7 +42,7 @@ private Absent() {
@Override
public Semigroup> checkedApply(Semigroup aSemigroup) {
- return LiftA2., Maybe>liftA2(aSemigroup)::apply;
+ return shortCircuitSemigroup(aSemigroup);
}
@SuppressWarnings("unchecked")
@@ -40,8 +50,8 @@ public static Absent absent() {
return (Absent) INSTANCE;
}
- public static Semigroup> absent(Semigroup semigroup) {
- return Absent.absent().apply(semigroup);
+ public static Semigroup> absent(Semigroup aSemigroup) {
+ return shortCircuitSemigroup(aSemigroup);
}
public static Fn1, Maybe> absent(Semigroup aSemigroup, Maybe x) {
@@ -51,4 +61,34 @@ public static Fn1, Maybe> absent(Semigroup aSemigroup, Maybe<
public static Maybe absent(Semigroup semigroup, Maybe x, Maybe y) {
return absent(semigroup, x).apply(y);
}
+
+ private static Semigroup> shortCircuitSemigroup(Semigroup aSemigroup) {
+ return new Semigroup>() {
+ @Override
+ public Maybe checkedApply(Maybe maybeX, Maybe maybeY) {
+ return liftA2(aSemigroup, maybeX, maybeY);
+ }
+
+ @Override
+ public Maybe foldLeft(Maybe acc, Iterable> maybes) {
+ return trampoline(
+ into((res, it) -> res.equals(nothing())
+ ? terminate(res)
+ : recurse(tuple(liftA2(aSemigroup, res, it.next()), it))),
+ tuple(acc, maybes.iterator()));
+ }
+
+ @Override
+ public Lazy> foldRight(Maybe accumulation, Iterable> as) {
+ boolean shouldShortCircuit = accumulation == nothing();
+ if (shouldShortCircuit)
+ return lazy(accumulation);
+ return FoldRight.foldRight(
+ (maybeX, acc) -> maybeX.lazyZip(acc.fmap(maybeY -> maybeY.fmap(aSemigroup.flip()))),
+ lazy(accumulation),
+ as
+ );
+ }
+ };
+ }
}
diff --git a/src/test/java/com/jnape/palatable/lambda/adt/TheseTest.java b/src/test/java/com/jnape/palatable/lambda/adt/TheseTest.java
index 36a195011..e9f764d49 100644
--- a/src/test/java/com/jnape/palatable/lambda/adt/TheseTest.java
+++ b/src/test/java/com/jnape/palatable/lambda/adt/TheseTest.java
@@ -12,9 +12,12 @@
import testsupport.traits.MonadRecLaws;
import testsupport.traits.TraversableLaws;
+import static com.jnape.palatable.lambda.adt.Maybe.just;
+import static com.jnape.palatable.lambda.adt.Maybe.nothing;
import static com.jnape.palatable.lambda.adt.These.a;
import static com.jnape.palatable.lambda.adt.These.b;
import static com.jnape.palatable.lambda.adt.These.both;
+import static com.jnape.palatable.lambda.adt.These.fromMaybes;
import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy;
import static com.jnape.palatable.traitor.framework.Subjects.subjects;
import static org.junit.Assert.assertEquals;
@@ -48,4 +51,12 @@ public void staticPure() {
These these = These.pureThese().apply(1);
assertEquals(b(1), these);
}
+
+ @Test
+ public void fromMaybesPermutations() {
+ assertEquals(nothing(), fromMaybes(nothing(), nothing()));
+ assertEquals(just(These.a(1)), fromMaybes(just(1), nothing()));
+ assertEquals(just(These.b(1)), fromMaybes(nothing(), just(1)));
+ assertEquals(just(These.both(1, "hello")), fromMaybes(just(1), just("hello")));
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java
index a0e974d43..7c2997ca6 100644
--- a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java
+++ b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java
@@ -48,6 +48,7 @@
import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.of;
import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.pureIterateT;
import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.singleton;
+import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.suspended;
import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.unfold;
import static com.jnape.palatable.lambda.monoid.builtin.AddAll.addAll;
import static com.jnape.palatable.lambda.monoid.builtin.Join.join;
@@ -313,4 +314,39 @@ public void flatMapCostsNoMoreEffortThanRequiredToYieldFirstValue() {
assertEquals(1, flatMapCost.get());
assertEquals(1, unfoldCost.get());
}
+
+ @Test
+ public void runStep() {
+ assertEquals(new Identity<>(nothing()),
+ IterateT., Integer>empty(pureIdentity())
+ ., IterateT, Integer>>>>>runStep());
+
+ Tuple2, IterateT, Integer>> singletonStep =
+ singleton(new Identity<>(1))
+ ., IterateT, Integer>>>>>runStep()
+ .runIdentity()
+ .orElseThrow(AssertionError::new);
+ assertEquals(just(1), singletonStep._1());
+ assertThat(singletonStep._2(), isEmpty());
+
+ Tuple2, IterateT, Integer>> emptySuspendedStep =
+ IterateT., Integer>suspended(() -> new Identity<>(nothing()),
+ pureIdentity())
+ ., IterateT, Integer>>>>>runStep()
+ .runIdentity()
+ .orElseThrow(AssertionError::new);
+
+ assertEquals(nothing(), emptySuspendedStep._1());
+ assertThat(emptySuspendedStep._2(), isEmpty());
+
+ Tuple2, IterateT, Integer>> nonEmptySuspendedStep =
+ suspended(() -> new Identity<>(just(tuple(1, empty(pureIdentity())))),
+ pureIdentity())
+ ., IterateT, Integer>>>>>runStep()
+ .runIdentity()
+ .orElseThrow(AssertionError::new);
+
+ assertEquals(just(1), nonEmptySuspendedStep._1());
+ assertThat(nonEmptySuspendedStep._2(), isEmpty());
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java
index 5ef25395e..5ab7b53f6 100644
--- a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java
+++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java
@@ -1,10 +1,17 @@
package com.jnape.palatable.lambda.semigroup.builtin;
+import com.jnape.palatable.lambda.adt.Maybe;
+import com.jnape.palatable.lambda.adt.Unit;
+import com.jnape.palatable.lambda.functions.builtin.fn1.Constantly;
import com.jnape.palatable.lambda.semigroup.Semigroup;
import org.junit.Test;
+import java.util.Arrays;
+
import static com.jnape.palatable.lambda.adt.Maybe.just;
import static com.jnape.palatable.lambda.adt.Maybe.nothing;
+import static com.jnape.palatable.lambda.adt.Unit.UNIT;
+import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat;
import static com.jnape.palatable.lambda.semigroup.builtin.Absent.absent;
import static org.junit.Assert.assertEquals;
@@ -12,12 +19,77 @@ public class AbsentTest {
@Test
public void semigroup() {
+ Semigroup addition = Integer::sum;
+
+ assertEquals(just(3), absent(addition, just(1), just(2)));
+ assertEquals(nothing(), absent(addition, nothing(), just(1)));
+ assertEquals(nothing(), absent(addition, just(1), nothing()));
+ assertEquals(nothing(), absent(addition, nothing(), nothing()));
+ }
+
+ @Test
+ public void foldRight() {
+ Absent absent = absent();
+ Semigroup addition = Integer::sum;
+
+ assertEquals(just(3), absent.apply(addition).foldRight(just(0), Arrays.asList(just(1), just(2))).value());
+ assertEquals(nothing(), absent.apply(addition).foldRight(just(0), Arrays.asList(nothing(), just(1))).value());
+ assertEquals(nothing(), absent.apply(addition).foldRight(just(0), Arrays.asList(just(1), nothing())).value());
+ assertEquals(nothing(), absent.apply(addition).foldRight(just(0), Arrays.asList(nothing(), nothing())).value());
+ }
+
+ @Test
+ public void foldLeft() {
Absent absent = absent();
Semigroup addition = Integer::sum;
- assertEquals(just(3), absent.apply(addition, just(1), just(2)));
- assertEquals(nothing(), absent.apply(addition, nothing(), just(1)));
- assertEquals(nothing(), absent.apply(addition, just(1), nothing()));
- assertEquals(nothing(), absent.apply(addition, nothing(), nothing()));
+ assertEquals(just(3), absent.apply(addition).foldLeft(just(0), Arrays.asList(just(1), just(2))));
+ assertEquals(nothing(), absent.apply(addition).foldLeft(just(0), Arrays.asList(nothing(), just(1))));
+ assertEquals(nothing(), absent.apply(addition).foldLeft(just(0), Arrays.asList(just(1), nothing())));
+ assertEquals(nothing(), absent.apply(addition).foldLeft(just(0), Arrays.asList(nothing(), nothing())));
+ }
+
+ @Test(timeout = 200)
+ public void foldRightShortCircuit() {
+ Maybe result = Absent.absent(Constantly::constantly)
+ .foldRight(just(UNIT), repeat(nothing())).value();
+ assertEquals(nothing(), result);
+
+ result = Absent.absent(Constantly::constantly)
+ .foldRight(nothing(), repeat(just(UNIT))).value();
+ assertEquals(nothing(), result);
+ }
+
+ @Test(timeout = 200)
+ public void foldLeftShortCircuit() {
+ Maybe result = Absent.absent(Constantly::constantly)
+ .foldLeft(just(UNIT), repeat(nothing()));
+ assertEquals(nothing(), result);
+
+ result = Absent.absent(Constantly::constantly)
+ .foldLeft(nothing(), repeat(just(UNIT)));
+ assertEquals(nothing(), result);
+ }
+
+ @Test(timeout = 200)
+ public void checkedApplyFoldRightShortCircuit() {
+ Maybe result = Absent.absent().checkedApply(Constantly::constantly)
+ .foldRight(just(UNIT), repeat(nothing())).value();
+ assertEquals(nothing(), result);
+
+ result = Absent.absent().checkedApply(Constantly::constantly)
+ .foldRight(nothing(), repeat(just(UNIT))).value();
+ assertEquals(nothing(), result);
+ }
+
+ @Test(timeout = 200)
+ public void checkedApplyFoldLeftShortCircuit() {
+ Maybe result = Absent.absent().checkedApply(Constantly::constantly)
+ .foldLeft(just(UNIT), repeat(nothing()));
+ assertEquals(nothing(), result);
+
+ result = Absent.absent().checkedApply(Constantly::constantly)
+ .foldLeft(nothing(), repeat(just(UNIT)));
+ assertEquals(nothing(), result);
}
}
\ No newline at end of file
diff --git a/src/test/java/testsupport/matchers/EitherMatcher.java b/src/test/java/testsupport/matchers/EitherMatcher.java
index 748d01be2..b18f68af6 100644
--- a/src/test/java/testsupport/matchers/EitherMatcher.java
+++ b/src/test/java/testsupport/matchers/EitherMatcher.java
@@ -9,11 +9,13 @@
import static com.jnape.palatable.lambda.adt.Either.right;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
import static com.jnape.palatable.lambda.io.IO.io;
+import static org.hamcrest.CoreMatchers.anything;
+import static org.hamcrest.CoreMatchers.equalTo;
public final class EitherMatcher extends TypeSafeMatcher> {
- private final Either, Matcher> matcher;
+ private final Either, Matcher super R>> matcher;
- private EitherMatcher(Either, Matcher> matcher) {
+ private EitherMatcher(Either, Matcher super R>> matcher) {
this.matcher = matcher;
}
@@ -44,11 +46,27 @@ public void describeTo(Description description) {
.unsafePerformIO();
}
- public static EitherMatcher isLeftThat(Matcher lMatcher) {
+ public static EitherMatcher isLeftThat(Matcher super L> lMatcher) {
return new EitherMatcher<>(left(lMatcher));
}
- public static EitherMatcher isRightThat(Matcher rMatcher) {
+ public static EitherMatcher isLeft() {
+ return isLeftThat(anything());
+ }
+
+ public static EitherMatcher isLeftOf(L l) {
+ return isLeftThat(equalTo(l));
+ }
+
+ public static EitherMatcher isRightThat(Matcher super R> rMatcher) {
return new EitherMatcher<>(right(rMatcher));
}
+
+ public static EitherMatcher isRight() {
+ return isRightThat(anything());
+ }
+
+ public static EitherMatcher isRightOf(R r) {
+ return isRightThat(equalTo(r));
+ }
}