diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/SemigroupFactory.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/SemigroupFactory.java
index 6d49d6a55..72314810e 100644
--- a/src/main/java/com/jnape/palatable/lambda/functions/specialized/SemigroupFactory.java
+++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/SemigroupFactory.java
@@ -1,6 +1,7 @@
package com.jnape.palatable.lambda.functions.specialized;
import com.jnape.palatable.lambda.functions.Fn3;
+import com.jnape.palatable.lambda.internal.Runtime;
import com.jnape.palatable.lambda.semigroup.Semigroup;
@FunctionalInterface
@@ -11,7 +12,11 @@ public interface SemigroupFactory extends Fn3 {
@Override
default Semigroup apply(A a) {
- return Fn3.super.apply(a)::apply;
+ try {
+ return checkedApply(a);
+ } catch (Throwable t) {
+ throw Runtime.throwChecked(t);
+ }
}
@Override
diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/ShortCircuitingSemigroup.java b/src/main/java/com/jnape/palatable/lambda/semigroup/ShortCircuitingSemigroup.java
new file mode 100644
index 000000000..3b1d4d4b5
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/semigroup/ShortCircuitingSemigroup.java
@@ -0,0 +1,35 @@
+package com.jnape.palatable.lambda.semigroup;
+
+import com.jnape.palatable.lambda.functions.recursion.RecursiveResult;
+import com.jnape.palatable.lambda.functor.builtin.Lazy;
+
+import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
+import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
+import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
+import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate;
+import static com.jnape.palatable.lambda.functions.recursion.Trampoline.trampoline;
+
+public interface ShortCircuitingSemigroup extends Semigroup {
+ RecursiveResult shortCircuitApply(A a1, A a2);
+
+ @Override
+ default A checkedApply(A a, A a2) throws Throwable {
+ return shortCircuitApply(a, a2).match(id(), id());
+ }
+
+ @Override
+ default A foldLeft(A a, Iterable as) {
+ return trampoline(
+ into((acc, it) -> !it.hasNext() ? terminate(acc) : shortCircuitApply(acc, it.next())
+ .>match(RecursiveResult::recurse,
+ RecursiveResult::terminate)
+ .biMapL(a3 -> tuple(a3, it))),
+ tuple(a, as.iterator()));
+ }
+
+ @Override
+ default Lazy foldRight(A a, Iterable as) {
+ return Semigroup.super.foldRight(a, as);
+ }
+
+}
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 68162ad74..baa324794 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
@@ -3,18 +3,20 @@
import com.jnape.palatable.lambda.adt.Maybe;
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.builtin.fn3.FoldRight;
+import com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2;
+import com.jnape.palatable.lambda.functions.recursion.RecursiveResult;
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 com.jnape.palatable.lambda.semigroup.ShortCircuitingSemigroup;
+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.hlist.HList.tuple;
-import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
+import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
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;
/**
@@ -63,19 +65,17 @@ public static Maybe absent(Semigroup semigroup, Maybe x, Maybe y
}
private static Semigroup> shortCircuitSemigroup(Semigroup aSemigroup) {
- return new Semigroup>() {
+ return new ShortCircuitingSemigroup>() {
@Override
- public Maybe checkedApply(Maybe maybeX, Maybe maybeY) {
- return liftA2(aSemigroup, maybeX, maybeY);
+ public RecursiveResult, Maybe> shortCircuitApply(Maybe a1, Maybe a2) {
+ return LiftA2., Maybe>liftA2(aSemigroup, a1, a2)
+ .match(constantly(terminate(nothing())),
+ a -> recurse(just(a)));
}
@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()));
+ public Maybe checkedApply(Maybe maybeX, Maybe maybeY) {
+ return liftA2(aSemigroup, maybeX, maybeY);
}
@Override
diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Collapse.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Collapse.java
index 7b163898d..9c6408848 100644
--- a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Collapse.java
+++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Collapse.java
@@ -4,9 +4,15 @@
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.specialized.BiSemigroupFactory;
import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory;
+import com.jnape.palatable.lambda.functor.builtin.Lazy;
+import com.jnape.palatable.lambda.internal.Runtime;
import com.jnape.palatable.lambda.monoid.Monoid;
import com.jnape.palatable.lambda.semigroup.Semigroup;
+import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
+import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map;
+import static com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2.liftA2;
+
/**
* A {@link Semigroup} instance formed by a {@link Tuple2}<_1, _2>
and semigroups over
* _1
and _2
. Successively collapses multiple {@link Tuple2}s into a single {@link Tuple2} by
@@ -26,12 +32,6 @@ public final class Collapse<_1, _2> implements BiSemigroupFactory,
private Collapse() {
}
- @Override
- public Semigroup> checkedApply(Semigroup<_1> _1Semigroup, Semigroup<_2> _2Semigroup) {
- return (x, y) -> x.biMap(_1Semigroup.flip().apply(y._1()),
- _2Semigroup.flip().apply(y._2()));
- }
-
@SuppressWarnings("unchecked")
public static <_1, _2> Collapse<_1, _2> collapse() {
return (Collapse<_1, _2>) INSTANCE;
@@ -56,4 +56,38 @@ public static <_1, _2> Tuple2<_1, _2> collapse(Semigroup<_1> _1Semigroup, Semigr
Tuple2<_1, _2> y) {
return collapse(_1Semigroup, _2Semigroup, x).apply(y);
}
+
+ @Override
+ public Semigroup> checkedApply(Semigroup<_1> _1Semigroup, Semigroup<_2> _2Semigroup) {
+ return new Semigroup>() {
+ @Override
+ public Tuple2<_1, _2> checkedApply(Tuple2<_1, _2> x, Tuple2<_1, _2> y) {
+ return x.biMap(_1Semigroup.flip().apply(y._1()),
+ _2Semigroup.flip().apply(y._2()));
+ }
+
+ @Override
+ public Tuple2<_1, _2> foldLeft(Tuple2<_1, _2> tuple2, Iterable> tuple2s) {
+ return tuple(_1Semigroup.foldLeft(tuple2._1(), map(Tuple2::_1, tuple2s)),
+ _2Semigroup.foldLeft(tuple2._2(), map(Tuple2::_2, tuple2s)));
+ }
+
+ @Override
+ public Lazy> foldRight(Tuple2<_1, _2> tuple2, Iterable> tuple2s) {
+ return liftA2(Tuple2::tuple,
+ _1Semigroup.foldRight(tuple2._1(), map(Tuple2::_1, tuple2s)),
+ _2Semigroup.foldRight(tuple2._2(), map(Tuple2::_2, tuple2s)));
+ }
+ };
+ }
+
+ @Override
+ public Semigroup> apply(Semigroup<_1> semigroup, Semigroup<_2> semigroup2) {
+ try {
+ return checkedApply(semigroup, semigroup2);
+ } catch (Throwable t) {
+ throw Runtime.throwChecked(t);
+ }
+ }
+
}
diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Intersection.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Intersection.java
index cf33e9b3a..c0b583059 100644
--- a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Intersection.java
+++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Intersection.java
@@ -2,13 +2,18 @@
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.builtin.fn1.Distinct;
-import com.jnape.palatable.lambda.semigroup.Semigroup;
+import com.jnape.palatable.lambda.functions.recursion.RecursiveResult;
+import com.jnape.palatable.lambda.semigroup.ShortCircuitingSemigroup;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Distinct.distinct;
+import static com.jnape.palatable.lambda.functions.builtin.fn1.Empty.empty;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Eq.eq;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Filter.filter;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Find.find;
+import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.recurse;
+import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate;
+import static java.util.Collections.emptyList;
/**
* Given two {@link Iterable Iterables} xs
and ys
, return the {@link Distinct distinct}
@@ -16,18 +21,13 @@
*
* @param the {@link Iterable} element type
*/
-public final class Intersection implements Semigroup> {
+public final class Intersection implements ShortCircuitingSemigroup> {
private static final Intersection> INSTANCE = new Intersection<>();
private Intersection() {
}
- @Override
- public Iterable checkedApply(Iterable xs, Iterable ys) {
- return filter(x -> find(eq(x), ys).fmap(constantly(true)).orElse(false), distinct(xs));
- }
-
@SuppressWarnings("unchecked")
public static Intersection intersection() {
return (Intersection) INSTANCE;
@@ -40,4 +40,16 @@ public static Fn1, Iterable> intersection(Iterable xs) {
public static Iterable intersection(Iterable xs, Iterable ys) {
return intersection(xs).apply(ys);
}
+
+ @Override
+ public RecursiveResult, Iterable> shortCircuitApply(Iterable a1, Iterable a2) {
+ if (empty(a1) || empty(a2))
+ return terminate(emptyList());
+ return recurse(filter(x -> find(eq(x), a1).fmap(constantly(true)).orElse(false), distinct(a2)));
+ }
+
+ @Override
+ public Iterable checkedApply(Iterable xs, Iterable ys) {
+ return filter(x -> find(eq(x), ys).fmap(constantly(true)).orElse(false), distinct(xs));
+ }
}
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 5ab7b53f6..c13b78f13 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
@@ -69,6 +69,12 @@ public void foldLeftShortCircuit() {
result = Absent.absent(Constantly::constantly)
.foldLeft(nothing(), repeat(just(UNIT)));
assertEquals(nothing(), result);
+
+ result = Absent.absent()
+ .apply(Constantly::constantly)
+ .foldLeft(nothing(), repeat(just(UNIT)));
+
+ assertEquals(nothing(), result);
}
@Test(timeout = 200)
diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/CollapseTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/CollapseTest.java
index cca4de936..6351e3957 100644
--- a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/CollapseTest.java
+++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/CollapseTest.java
@@ -1,10 +1,24 @@
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.adt.hlist.Tuple2;
+import com.jnape.palatable.lambda.functions.builtin.fn1.Constantly;
+import com.jnape.palatable.lambda.functor.builtin.Lazy;
import com.jnape.palatable.lambda.semigroup.Semigroup;
import org.junit.Test;
+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.adt.hlist.HList.tuple;
+import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat;
+import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons;
+import static com.jnape.palatable.lambda.functions.builtin.fn2.Zip.zip;
+import static com.jnape.palatable.lambda.monoid.builtin.And.and;
import static com.jnape.palatable.lambda.monoid.builtin.Join.join;
+import static com.jnape.palatable.lambda.monoid.builtin.Or.or;
+import static com.jnape.palatable.lambda.semigroup.builtin.Absent.absent;
import static com.jnape.palatable.lambda.semigroup.builtin.Collapse.collapse;
import static org.junit.Assert.assertEquals;
@@ -12,10 +26,38 @@ public class CollapseTest {
@Test
public void semigroup() {
- Semigroup join = join();
- Semigroup add = Integer::sum;
+ Semigroup join = join();
+ Semigroup add = Integer::sum;
Collapse collapse = collapse();
assertEquals(tuple("foobar", 3), collapse.apply(join, add, tuple("foo", 1), tuple("bar", 2)));
}
+
+ @Test(timeout = 200)
+ public void foldLeftShortCircuits() {
+ Semigroup> collapse = collapse(and(), or());
+ Iterable> tuples = zip(cons(false, repeat(true)),
+ cons(true, repeat(false)));
+ Tuple2 booleanBooleanTuple2 = collapse.foldLeft(tuple(true, false),
+ tuples);
+ assertEquals(tuple(false, true),
+ booleanBooleanTuple2);
+ }
+
+ @Test(timeout = 200)
+ public void foldRightShortCircuits() {
+ Semigroup> absent = absent(Constantly::constantly);
+ Semigroup, Maybe>> collapse = collapse(absent, absent);
+
+ Lazy, Maybe>> maybeLazy = collapse
+ .foldRight(tuple(just(UNIT), just(UNIT)),
+ repeat(tuple(nothing(), nothing())));
+ Tuple2, Maybe> result = maybeLazy.value();
+ assertEquals(tuple(nothing(), nothing()), result);
+
+ result = collapse
+ .foldRight(tuple(nothing(), nothing()),
+ repeat(tuple(just(UNIT), just(UNIT)))).value();
+ assertEquals(tuple(nothing(), nothing()), result);
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/IntersectionTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/IntersectionTest.java
index 0ecd33c71..8710b6a0b 100644
--- a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/IntersectionTest.java
+++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/IntersectionTest.java
@@ -1,5 +1,6 @@
package com.jnape.palatable.lambda.semigroup.builtin;
+import com.jnape.palatable.lambda.adt.Unit;
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.traitor.annotations.TestTraits;
import com.jnape.palatable.traitor.runners.Traits;
@@ -10,6 +11,8 @@
import testsupport.traits.InfiniteIterableSupport;
import testsupport.traits.Laziness;
+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.Intersection.intersection;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@@ -40,4 +43,10 @@ public void intersectionIsCommonElementsAcrossIterables() {
assertThat(intersection(singletonList(1), singletonList(2)), isEmpty());
assertThat(intersection(asList(1, 2, 3, 3), singletonList(3)), iterates(3));
}
+
+ @Test(timeout = 200)
+ public void foldLeftShortCircuits() {
+ assertThat(Intersection.intersection().foldLeft(emptyList(), repeat(singletonList(UNIT))), isEmpty());
+ assertThat(Intersection.intersection().foldLeft(singletonList(UNIT), repeat(emptyList())), isEmpty());
+ }
}
\ No newline at end of file