Skip to content

Commit 7af1b9c

Browse files
authored
Absent folds short-circuit (palatable#116)
1 parent 777949b commit 7af1b9c

File tree

2 files changed

+120
-8
lines changed
  • src
    • main/java/com/jnape/palatable/lambda/semigroup/builtin
    • test/java/com/jnape/palatable/lambda/semigroup/builtin

2 files changed

+120
-8
lines changed

src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,21 @@
22

33
import com.jnape.palatable.lambda.adt.Maybe;
44
import com.jnape.palatable.lambda.functions.Fn1;
5-
import com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2;
5+
import com.jnape.palatable.lambda.functions.builtin.fn3.FoldRight;
66
import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory;
7+
import com.jnape.palatable.lambda.functor.builtin.Lazy;
78
import com.jnape.palatable.lambda.monoid.builtin.Present;
89
import com.jnape.palatable.lambda.semigroup.Semigroup;
910

11+
import static com.jnape.palatable.lambda.adt.Maybe.nothing;
12+
import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
13+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
14+
import static com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2.liftA2;
15+
import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.recurse;
16+
import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate;
17+
import static com.jnape.palatable.lambda.functions.recursion.Trampoline.trampoline;
18+
import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy;
19+
1020
/**
1121
* A {@link Semigroup} instance formed by <code>{@link Maybe}&lt;A&gt;</code> and a semigroup over <code>A</code>. The
1222
* application to two {@link Maybe} values is absence-biased, such that for a given {@link Maybe} <code>x</code>
@@ -32,16 +42,16 @@ private Absent() {
3242

3343
@Override
3444
public Semigroup<Maybe<A>> checkedApply(Semigroup<A> aSemigroup) {
35-
return LiftA2.<A, A, A, Maybe<?>, Maybe<A>>liftA2(aSemigroup)::apply;
45+
return shortCircuitSemigroup(aSemigroup);
3646
}
3747

3848
@SuppressWarnings("unchecked")
3949
public static <A> Absent<A> absent() {
4050
return (Absent<A>) INSTANCE;
4151
}
4252

43-
public static <A> Semigroup<Maybe<A>> absent(Semigroup<A> semigroup) {
44-
return Absent.<A>absent().apply(semigroup);
53+
public static <A> Semigroup<Maybe<A>> absent(Semigroup<A> aSemigroup) {
54+
return shortCircuitSemigroup(aSemigroup);
4555
}
4656

4757
public static <A> Fn1<Maybe<A>, Maybe<A>> absent(Semigroup<A> aSemigroup, Maybe<A> x) {
@@ -51,4 +61,34 @@ public static <A> Fn1<Maybe<A>, Maybe<A>> absent(Semigroup<A> aSemigroup, Maybe<
5161
public static <A> Maybe<A> absent(Semigroup<A> semigroup, Maybe<A> x, Maybe<A> y) {
5262
return absent(semigroup, x).apply(y);
5363
}
64+
65+
private static <A> Semigroup<Maybe<A>> shortCircuitSemigroup(Semigroup<A> aSemigroup) {
66+
return new Semigroup<Maybe<A>>() {
67+
@Override
68+
public Maybe<A> checkedApply(Maybe<A> maybeX, Maybe<A> maybeY) {
69+
return liftA2(aSemigroup, maybeX, maybeY);
70+
}
71+
72+
@Override
73+
public Maybe<A> foldLeft(Maybe<A> acc, Iterable<Maybe<A>> maybes) {
74+
return trampoline(
75+
into((res, it) -> res.equals(nothing())
76+
? terminate(res)
77+
: recurse(tuple(liftA2(aSemigroup, res, it.next()), it))),
78+
tuple(acc, maybes.iterator()));
79+
}
80+
81+
@Override
82+
public Lazy<Maybe<A>> foldRight(Maybe<A> accumulation, Iterable<Maybe<A>> as) {
83+
boolean shouldShortCircuit = accumulation == nothing();
84+
if (shouldShortCircuit)
85+
return lazy(accumulation);
86+
return FoldRight.foldRight(
87+
(maybeX, acc) -> maybeX.lazyZip(acc.fmap(maybeY -> maybeY.fmap(aSemigroup.flip()))),
88+
lazy(accumulation),
89+
as
90+
);
91+
}
92+
};
93+
}
5494
}
Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,95 @@
11
package com.jnape.palatable.lambda.semigroup.builtin;
22

3+
import com.jnape.palatable.lambda.adt.Maybe;
4+
import com.jnape.palatable.lambda.adt.Unit;
5+
import com.jnape.palatable.lambda.functions.builtin.fn1.Constantly;
36
import com.jnape.palatable.lambda.semigroup.Semigroup;
47
import org.junit.Test;
58

9+
import java.util.Arrays;
10+
611
import static com.jnape.palatable.lambda.adt.Maybe.just;
712
import static com.jnape.palatable.lambda.adt.Maybe.nothing;
13+
import static com.jnape.palatable.lambda.adt.Unit.UNIT;
14+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat;
815
import static com.jnape.palatable.lambda.semigroup.builtin.Absent.absent;
916
import static org.junit.Assert.assertEquals;
1017

1118
public class AbsentTest {
1219

1320
@Test
1421
public void semigroup() {
22+
Semigroup<Integer> addition = Integer::sum;
23+
24+
assertEquals(just(3), absent(addition, just(1), just(2)));
25+
assertEquals(nothing(), absent(addition, nothing(), just(1)));
26+
assertEquals(nothing(), absent(addition, just(1), nothing()));
27+
assertEquals(nothing(), absent(addition, nothing(), nothing()));
28+
}
29+
30+
@Test
31+
public void foldRight() {
32+
Absent<Integer> absent = absent();
33+
Semigroup<Integer> addition = Integer::sum;
34+
35+
assertEquals(just(3), absent.apply(addition).foldRight(just(0), Arrays.asList(just(1), just(2))).value());
36+
assertEquals(nothing(), absent.apply(addition).foldRight(just(0), Arrays.asList(nothing(), just(1))).value());
37+
assertEquals(nothing(), absent.apply(addition).foldRight(just(0), Arrays.asList(just(1), nothing())).value());
38+
assertEquals(nothing(), absent.apply(addition).foldRight(just(0), Arrays.asList(nothing(), nothing())).value());
39+
}
40+
41+
@Test
42+
public void foldLeft() {
1543
Absent<Integer> absent = absent();
1644
Semigroup<Integer> addition = Integer::sum;
1745

18-
assertEquals(just(3), absent.apply(addition, just(1), just(2)));
19-
assertEquals(nothing(), absent.apply(addition, nothing(), just(1)));
20-
assertEquals(nothing(), absent.apply(addition, just(1), nothing()));
21-
assertEquals(nothing(), absent.apply(addition, nothing(), nothing()));
46+
assertEquals(just(3), absent.apply(addition).foldLeft(just(0), Arrays.asList(just(1), just(2))));
47+
assertEquals(nothing(), absent.apply(addition).foldLeft(just(0), Arrays.asList(nothing(), just(1))));
48+
assertEquals(nothing(), absent.apply(addition).foldLeft(just(0), Arrays.asList(just(1), nothing())));
49+
assertEquals(nothing(), absent.apply(addition).foldLeft(just(0), Arrays.asList(nothing(), nothing())));
50+
}
51+
52+
@Test(timeout = 200)
53+
public void foldRightShortCircuit() {
54+
Maybe<Unit> result = Absent.<Unit>absent(Constantly::constantly)
55+
.foldRight(just(UNIT), repeat(nothing())).value();
56+
assertEquals(nothing(), result);
57+
58+
result = Absent.<Unit>absent(Constantly::constantly)
59+
.foldRight(nothing(), repeat(just(UNIT))).value();
60+
assertEquals(nothing(), result);
61+
}
62+
63+
@Test(timeout = 200)
64+
public void foldLeftShortCircuit() {
65+
Maybe<Unit> result = Absent.<Unit>absent(Constantly::constantly)
66+
.foldLeft(just(UNIT), repeat(nothing()));
67+
assertEquals(nothing(), result);
68+
69+
result = Absent.<Unit>absent(Constantly::constantly)
70+
.foldLeft(nothing(), repeat(just(UNIT)));
71+
assertEquals(nothing(), result);
72+
}
73+
74+
@Test(timeout = 200)
75+
public void checkedApplyFoldRightShortCircuit() {
76+
Maybe<Unit> result = Absent.<Unit>absent().checkedApply(Constantly::constantly)
77+
.foldRight(just(UNIT), repeat(nothing())).value();
78+
assertEquals(nothing(), result);
79+
80+
result = Absent.<Unit>absent().checkedApply(Constantly::constantly)
81+
.foldRight(nothing(), repeat(just(UNIT))).value();
82+
assertEquals(nothing(), result);
83+
}
84+
85+
@Test(timeout = 200)
86+
public void checkedApplyFoldLeftShortCircuit() {
87+
Maybe<Unit> result = Absent.<Unit>absent().checkedApply(Constantly::constantly)
88+
.foldLeft(just(UNIT), repeat(nothing()));
89+
assertEquals(nothing(), result);
90+
91+
result = Absent.<Unit>absent().checkedApply(Constantly::constantly)
92+
.foldLeft(nothing(), repeat(just(UNIT)));
93+
assertEquals(nothing(), result);
2294
}
2395
}

0 commit comments

Comments
 (0)