From 39356f4422b628486f597472c7cf219cfffb6bf2 Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Thu, 9 Jun 2022 18:04:57 -0500 Subject: [PATCH 1/4] Have SemigroupFactory::apply delegate to SemigroupFactory::checkedApply Co-authored-by: Skyler Lutz --- .../lambda/functions/specialized/SemigroupFactory.java | 7 ++++++- .../palatable/lambda/semigroup/builtin/AbsentTest.java | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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/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) From 117168bceea10d25b004c7c7c76ff582aac9b979 Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Thu, 9 Jun 2022 19:25:48 -0500 Subject: [PATCH 2/4] Collapse delegates to it's semigroups for foldLeft/foldRight Co-authored-by: Skyler Lutz --- .../lambda/semigroup/builtin/Collapse.java | 46 ++++++++++++++++--- .../semigroup/builtin/CollapseTest.java | 46 ++++++++++++++++++- 2 files changed, 84 insertions(+), 8 deletions(-) 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/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 From 6e9ee4a9002f5b1c89a0198e74244b46ef443876 Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Fri, 10 Jun 2022 09:32:15 -0500 Subject: [PATCH 3/4] Spike of ShortCircuitingSemigroup Co-authored-by: Skyler Lutz --- .../semigroup/ShortCircuitingSemigroup.java | 35 +++++++++++++++++++ .../lambda/semigroup/builtin/Absent.java | 24 ++++++------- .../semigroup/builtin/Intersection.java | 21 +++++++---- .../semigroup/builtin/IntersectionTest.java | 9 +++++ 4 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/ShortCircuitingSemigroup.java 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/Intersection.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Intersection.java index cf33e9b3a..2e5bbf4a3 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,11 @@ 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))); + } } 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 From 5caba87652532a61093c3b6704bae938ddb7559a Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Fri, 10 Jun 2022 09:40:37 -0500 Subject: [PATCH 4/4] Recover intersection laziness behavior --- .../palatable/lambda/semigroup/builtin/Intersection.java | 5 +++++ 1 file changed, 5 insertions(+) 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 2e5bbf4a3..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 @@ -47,4 +47,9 @@ public RecursiveResult, Iterable> shortCircuitApply(Iterable a 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)); + } }