Skip to content

Commit 85a79ef

Browse files
committed
Short-circuiting in relevant monoids
1 parent 3da9097 commit 85a79ef

File tree

8 files changed

+86
-0
lines changed

8 files changed

+86
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
77
### Changed
88
- `ToMap` accepts an `Iterable` covariant in `Map.Entry`
99
- `RecursiveResult#invert` is also a `RecursiveResult`
10+
- `First`/`And`/`Or` monoids all utilize short-circuiting
11+
- `Monoid#foldLeft/foldRight` delegate to `Monoid#reduceLeft/reduceRight`, respectively
1012

1113
### Added
1214
- `Upcast` for safely casting up a type hierarchy

src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import java.util.function.Function;
99
import java.util.function.Supplier;
1010

11+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons;
1112
import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map;
13+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Snoc.snoc;
1214

1315
/**
1416
* A {@link Monoid} is the pairing of a {@link Semigroup} with an identity element.
@@ -64,6 +66,22 @@ default <B> A foldMap(Function<? super B, ? extends A> fn, Iterable<B> bs) {
6466
return reduceLeft(map(fn, bs));
6567
}
6668

69+
/**
70+
* {@inheritDoc}
71+
*/
72+
@Override
73+
default A foldLeft(A a, Iterable<A> as) {
74+
return reduceLeft(cons(a, as));
75+
}
76+
77+
/**
78+
* {@inheritDoc}
79+
*/
80+
@Override
81+
default A foldRight(A a, Iterable<A> as) {
82+
return reduceRight(snoc(a, as));
83+
}
84+
6785
/**
6886
* {@inheritDoc}
6987
*/

src/main/java/com/jnape/palatable/lambda/monoid/builtin/And.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
import com.jnape.palatable.lambda.functions.specialized.BiPredicate;
55
import com.jnape.palatable.lambda.monoid.Monoid;
66

7+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
8+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Not.not;
9+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Find.find;
10+
711
/**
812
* A {@link Monoid} instance formed by <code>Boolean</code>. Equivalent to logical <code>&amp;&amp;</code>.
913
*
@@ -27,6 +31,11 @@ public Boolean apply(Boolean x, Boolean y) {
2731
return x && y;
2832
}
2933

34+
@Override
35+
public Boolean reduceLeft(Iterable<Boolean> bools) {
36+
return find(not(id()), bools).orElse(true);
37+
}
38+
3039
@Override
3140
public boolean test(Boolean x, Boolean y) {
3241
return apply(x, y);

src/main/java/com/jnape/palatable/lambda/monoid/builtin/First.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import com.jnape.palatable.lambda.monoid.Monoid;
66

77
import static com.jnape.palatable.lambda.adt.Maybe.nothing;
8+
import static com.jnape.palatable.lambda.functions.builtin.fn1.CatMaybes.catMaybes;
9+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Head.head;
810

911
/**
1012
* A {@link Monoid} instance formed by <code>{@link Maybe}&lt;A&gt;</code>. The application to two {@link Maybe} values
@@ -33,6 +35,11 @@ public Maybe<A> apply(Maybe<A> x, Maybe<A> y) {
3335
return x.fmap(Maybe::just).orElse(y);
3436
}
3537

38+
@Override
39+
public Maybe<A> reduceLeft(Iterable<Maybe<A>> maybes) {
40+
return head(catMaybes(maybes));
41+
}
42+
3643
@SuppressWarnings("unchecked")
3744
public static <A> First<A> first() {
3845
return INSTANCE;

src/main/java/com/jnape/palatable/lambda/monoid/builtin/Or.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import com.jnape.palatable.lambda.functions.specialized.BiPredicate;
55
import com.jnape.palatable.lambda.monoid.Monoid;
66

7+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
8+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Find.find;
9+
710
/**
811
* A {@link Monoid} instance formed by <code>Boolean</code>. Equivalent to logical <code>||</code>.
912
*
@@ -32,6 +35,11 @@ public boolean test(Boolean x, Boolean y) {
3235
return apply(x, y);
3336
}
3437

38+
@Override
39+
public Boolean reduceLeft(Iterable<Boolean> bools) {
40+
return find(id(), bools).orElse(false);
41+
}
42+
3543
@Override
3644
public Or flip() {
3745
return this;

src/test/java/com/jnape/palatable/lambda/monoid/builtin/AndTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import org.junit.Test;
44

5+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
6+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat;
7+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons;
58
import static com.jnape.palatable.lambda.monoid.builtin.And.and;
69
import static org.junit.Assert.assertEquals;
710

@@ -20,4 +23,15 @@ public void monoid() {
2023
assertEquals(false, and.apply(true, false));
2124
assertEquals(false, and.apply(false, false));
2225
}
26+
27+
@Test(timeout = 500)
28+
public void shortCircuiting() {
29+
Iterable<Boolean> bools = cons(false, repeat(true));
30+
And and = and();
31+
32+
assertEquals(false, and.foldLeft(false, bools));
33+
assertEquals(false, and.foldLeft(true, bools));
34+
assertEquals(false, and.reduceLeft(bools));
35+
assertEquals(false, and.foldMap(id(), bools));
36+
}
2337
}

src/test/java/com/jnape/palatable/lambda/monoid/builtin/FirstTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package com.jnape.palatable.lambda.monoid.builtin;
22

3+
import com.jnape.palatable.lambda.adt.Maybe;
34
import org.junit.Test;
45

56
import static com.jnape.palatable.lambda.adt.Maybe.just;
67
import static com.jnape.palatable.lambda.adt.Maybe.nothing;
8+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
9+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat;
710
import static com.jnape.palatable.lambda.monoid.builtin.First.first;
811
import static org.junit.Assert.assertEquals;
912

@@ -22,4 +25,15 @@ public void monoid() {
2225
assertEquals(just(2), first.apply(nothing(), just(2)));
2326
assertEquals(nothing(), first.apply(nothing(), nothing()));
2427
}
28+
29+
@Test(timeout = 500)
30+
public void shortCircuiting() {
31+
Iterable<Maybe<Integer>> maybeInts = repeat(just(1));
32+
First<Integer> first = First.first();
33+
34+
assertEquals(just(1), first.foldLeft(nothing(), maybeInts));
35+
assertEquals(just(1), first.foldLeft(just(1), maybeInts));
36+
assertEquals(just(1), first.reduceLeft(maybeInts));
37+
assertEquals(just(1), first.foldMap(id(), maybeInts));
38+
}
2539
}

src/test/java/com/jnape/palatable/lambda/monoid/builtin/OrTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import org.junit.Test;
44

5+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
6+
import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat;
7+
import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons;
58
import static com.jnape.palatable.lambda.monoid.builtin.Or.or;
69
import static org.junit.Assert.assertEquals;
710

@@ -20,4 +23,15 @@ public void monoid() {
2023
assertEquals(true, or.apply(false, true));
2124
assertEquals(false, or.apply(false, false));
2225
}
26+
27+
@Test(timeout = 500)
28+
public void shortCircuiting() {
29+
Iterable<Boolean> bools = cons(true, repeat(false));
30+
Or or = or();
31+
32+
assertEquals(true, or.foldLeft(false, bools));
33+
assertEquals(true, or.foldLeft(true, bools));
34+
assertEquals(true, or.reduceLeft(bools));
35+
assertEquals(true, or.foldMap(id(), bools));
36+
}
2337
}

0 commit comments

Comments
 (0)