Skip to content

Instantly share code, notes, and snippets.

@SegFaultAX
Last active October 6, 2018 18:23
Show Gist options
  • Save SegFaultAX/924dffca5d484a25871d8317391cc51f to your computer and use it in GitHub Desktop.
Save SegFaultAX/924dffca5d484a25871d8317391cc51f to your computer and use it in GitHub Desktop.
Functional composable applicative streaming monoidal folds [Java]
import java.util.function.BiFunction;
import java.util.function.Supplier;
public interface Monoid<T> extends Semigroup<T> {
T empty();
static <T> Monoid<T> of(Supplier<T> v, BiFunction<T, T, T> app) {
return new Monoid<>() {
@Override
public T empty() {
return v.get();
}
@Override
public T append(T a, T b) {
return app.apply(a, b);
}
};
}
}
import java.util.Collection;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public interface MonoidFold<Acc, From, To> {
Monoid<Acc> ev();
Acc quantify(From x);
To combine(Acc acc);
static <A, F, T> MonoidFold<A, F, T> using(Monoid<A> m, Function<F, A> q, Function<A, T> c) {
return new MonoidFold<>() {
@Override
public Monoid<A> ev() {
return m;
}
@Override
public A quantify(F x) {
return q.apply(x);
}
@Override
public T combine(A a) {
return c.apply(a);
}
};
}
static <A, F> MonoidFold<A, F, A> using(Monoid<A> m, Function<F, A> q) {
return using(m, q, Function.identity());
}
static <A> MonoidFold<A, A, A> using(Monoid<A> m) {
return using(m, Function.identity(), Function.identity());
}
static <A, F, T> T fold(MonoidFold<A, F, T> f, Collection<F> xs) {
A acc = f.ev().empty();
for (F x : xs) {
acc = f.ev().append(acc, f.quantify(x));
}
return f.combine(acc);
}
default To fold(Collection<From> xs) {
return fold(this, xs);
}
static <A, F, T1, T2> MonoidFold<A, F, T2> map(MonoidFold<A, F, T1> f, Function<T1, T2> fn) {
return new MonoidFold<>() {
@Override
public Monoid<A> ev() {
return f.ev();
}
@Override
public A quantify(F x) {
return f.quantify(x);
}
@Override
public T2 combine(A a) {
return fn.apply(f.combine(a));
}
};
}
default <R> MonoidFold<Acc, From, R> map(Function<To, R> fn) {
return MonoidFold.map(this, fn);
}
static <F, T> MonoidFold<Void, F, T> pure(T v) {
return new MonoidFold<>() {
@Override
public Monoid<Void> ev() {
return Monoid.of(() -> null, (a, b) -> null);
}
@Override
public Void quantify(F x) {
return null;
}
@Override
public T combine(Void aVoid) {
return v;
}
};
}
static <A1, A2, F, T1, T2> MonoidFold<Pair<A1, A2>, F, T2> apply(MonoidFold<A1, F, Function<T1, T2>> f, MonoidFold<A2, F, T1> v) {
return new MonoidFold<>() {
@Override
public Monoid<Pair<A1, A2>> ev() {
return new Monoid<>() {
@Override
public Pair<A1, A2> empty() {
return Pair.of(f.ev().empty(), v.ev().empty());
}
@Override
public Pair<A1, A2> append(Pair<A1, A2> a, Pair<A1, A2> b) {
return Pair.of(f.ev().append(a.left(), b.left()), v.ev().append(a.right(), b.right()));
}
};
}
@Override
public Pair<A1, A2> quantify(F x) {
return Pair.of(f.quantify(x), v.quantify(x));
}
@Override
public T2 combine(Pair<A1, A2> acc) {
return f.combine(acc.left()).apply(v.combine(acc.right()));
}
};
}
static <A1, A2, T1, T2, F, R> MonoidFold<Pair<A1, A2>, F, R> liftA2(
BiFunction<T1, T2, R> fn,
MonoidFold<A1, F, T1> f1,
MonoidFold<A2, F, T2> f2) {
Function<T1, Function<T2, R>> curried = a -> b -> fn.apply(a, b);
return MonoidFold.apply(f1.map(curried), f2);
}
static <A1, A2, T1, T2, F, R> MonoidFold<Pair<A1, A2>, F, R> liftA2(
Function<T1, Function<T2, R>> fn,
MonoidFold<A1, F, T1> f1,
MonoidFold<A2, F, T2> f2) {
return MonoidFold.apply(f1.map(fn), f2);
}
}
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class MonoidFoldTest {
private final Monoid<Integer> monoidSum = Monoid.of(() -> 0, (a, b) -> a + b);
private final Monoid<Integer> monoidProduct = Monoid.of(() -> 1, (a, b) -> a * b);
private final MonoidFold<Integer, Integer, Integer> sumFold = MonoidFold.using(monoidSum);
private final MonoidFold<Integer, Integer, Integer> productFold = MonoidFold.using(monoidProduct);
@Test
public void testSimpleMonoidFold() {
assertThat(sumFold.fold(List.of(1, 2, 3, 4))).isEqualTo(10);
}
@Test
public void testMonoidFoldFunctor() {
assertThat(sumFold.map(v -> Integer.toString(v)).fold(List.of(1, 2, 3, 4))).isEqualTo("10");
}
@Test
public void testMonoidFoldApplicativePure() {
assertThat(Fold.pure(10).fold(List.of(1, 2))).isEqualTo(10);
}
@Test
public void testMonoidFoldApplicativeApply() {
MonoidFold<Pair<Pair<Void, Integer>, Integer>, Integer, Pair<Integer, Integer>> tuplingSumProduct =
MonoidFold.apply(MonoidFold.apply(MonoidFold.pure(Pair.curried()), sumFold), productFold);
assertThat(tuplingSumProduct.fold(List.of(1, 2, 3, 4))).isEqualTo(Pair.of(10, 24));
}
@Test
public void testMonoidFoldApplicativeLift() {
MonoidFold<Pair<Integer, Integer>, Integer, Pair<Integer, Integer>> tuplingSumProduct =
MonoidFold.liftA2(Pair::of, sumFold, productFold);
assertThat(tuplingSumProduct.fold(List.of(1, 2, 3, 4))).isEqualTo(Pair.of(10, 24));
}
@Test
public void testMonoidFoldCustomQuantify() {
MonoidFold<Integer, Object, Integer> lenFold = MonoidFold.using(monoidSum, v -> 1);
assertThat(lenFold.fold(List.of(1, 2, 3, 4))).isEqualTo(4);
}
}
import java.util.Objects;
import java.util.function.Function;
public class Pair<A, B> {
private final A a;
private final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
public static <A, B> Pair<A, B> of(A a, B b) {
return new Pair<>(a, b);
}
public static <A, B> Function<A, Function<B, Pair<A, B>>> curried() {
return a -> b -> of(a, b);
}
public A left() {
return a;
}
public B right() {
return b;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(a, pair.a) &&
Objects.equals(b, pair.b);
}
@Override
public int hashCode() {
return Objects.hash(a, b);
}
@Override
public String toString() {
return "Pair{" +
"a=" + a +
", b=" + b +
'}';
}
}
public interface Semigroup<T> {
T append(T a, T b);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment