fj.Monoid Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functionaljava Show documentation
Show all versions of functionaljava Show documentation
Functional Java is an open source library that supports closures for the Java programming language
package fj;
import static fj.F1Functions.dimap;
import fj.data.Array;
import fj.data.DList;
import fj.data.List;
import fj.data.IO;
import fj.data.Natural;
import fj.data.Option;
import fj.data.Set;
import fj.data.Stream;
import static fj.Function.*;
import static fj.Semigroup.semigroupDef;
import static fj.Unit.unit;
import static fj.data.List.nil;
import static fj.data.Natural.natural;
import static fj.data.Option.none;
import static fj.data.Stream.iterableStream;
import java.math.BigInteger;
import java.math.BigDecimal;
/**
* A monoid abstraction to be defined across types of the given type argument. Implementations must
* follow the monoidal laws:
*
* - Left Identity; forall x. sum(zero(), x) == x
* - Right Identity; forall x. sum(x, zero()) == x
* - Associativity; forall x y z. sum(sum(x, y), z) == sum(x, sum(y, z))
*
*
* @version %build.number%
*/
public final class Monoid {
private final Definition def;
/**
* Primitives functions of Monoid: minimal definition and overridable methods.
*/
public interface Definition extends Semigroup.Definition {
A empty();
default A sum(F0> as) {
return as.f().foldLeft(this::append, empty());
}
@Override
default A sum(A a, F0> as) {
return sum(() -> Stream.cons(a, as));
}
default A multiply(int n, A a) {
return (n <= 0)
? empty()
: Semigroup.Definition.super.multiply1p(n - 1, a);
}
@Override
default A multiply1p(int n, A a) {
return n == Integer.MAX_VALUE
? append(a, multiply(n, a))
: multiply(n + 1, a);
}
default Definition dual() {
return new Definition(){
@Override
public A empty() {
return Definition.this.empty();
}
@Override
public A append(A a1, A a2) {
return Definition.this.append(a2, a1);
}
@Override
public A multiply(int n, A a) {
return Definition.this.multiply(n, a);
}
@Override
public Definition dual() {
return Definition.this;
}
};
}
}
/**
* Primitives functions of Monoid: alternative minimal definition and overridable methods.
*/
public interface AltDefinition extends Definition {
@Override
F prepend(A a);
@Override
default A append(A a1, A a2) {
return prepend(a1).f(a2);
}
}
private Monoid(Definition def) {
this.def = def;
}
/**
* Composes this monoid with another.
*/
public Monoid>compose(Monoid m) {
return compose(m, P2.__1(), P2.__2(), P::p);
}
/**
* Returns a semigroup projection of this monoid.
*
* @return A semigroup projection of this monoid.
*/
public Semigroup semigroup() {
return semigroupDef(def);
}
/**
* Maps the given functions across this monoid as an invariant functor.
*
* @param f The covariant map.
* @param g The contra-variant map.
* @return A new monoid.
*/
public Monoid xmap(final F f, final F g) {
Monoid.Definition def = this.def;
B zero = f.f(def.empty());
return monoidDef(new Definition() {
@Override
public B empty() {
return zero;
}
@Override
public B append(B a1, B a2) {
return f.f(def.append(g.f(a1), g.f(a2)));
}
@Override
public F prepend(B b) {
return dimap(def.prepend(g.f(b)), g, f);
}
@Override
public B multiply(int n, B b) {
return f.f(def.multiply(n , g.f(b)));
}
@Override
public B sum(F0> as) {
return f.f(def.sum(() -> as.f().map(g)));
}
});
}
public Monoid compose(Monoid mb, final F a, final F b, final F2 c) {
Definition maDef = this.def;
Definition mbDef = mb.def;
C empty = c.f(maDef.empty(), mbDef.empty());
return monoidDef(new Definition() {
@Override
public C empty() {
return empty;
}
@Override
public C append(C c1, C c2) {
return c.f(maDef.append(a.f(c1), a.f(c2)), mbDef.append(b.f(c1), b.f(c2)));
}
@Override
public F prepend(C c1) {
F prependA = maDef.prepend(a.f(c1));
F prependB = mbDef.prepend(b.f(c1));
return c2 -> c.f(prependA.f(a.f(c2)), prependB.f(b.f(c2)));
}
@Override
public C multiply(int n, C c1) {
return c.f(maDef.multiply(n, a.f(c1)), mbDef.multiply(n, b.f(c1)));
}
@Override
public C sum(F0> cs) {
return c.f(maDef.sum(() -> cs.f().map(a)), mbDef.sum(() -> cs.f().map(b)));
}
});
}
/**
* Sums the two given arguments.
*
* @param a1 A value to sum with another.
* @param a2 A value to sum with another.
* @return The of the two given arguments.
*/
public A sum(final A a1, final A a2) {
return def.append(a1, a2);
}
/**
* Returns a function that sums the given value according to this monoid.
*
* @param a1 The value to sum.
* @return A function that sums the given value according to this monoid.
*/
public F sum(final A a1) {
return def.prepend(a1);
}
/**
* Returns a function that sums according to this monoid.
*
* @return A function that sums according to this monoid.
*/
public F> sum() {
return def::prepend;
}
/**
* The zero value for this monoid.
*
* @return The zero value for this monoid.
*/
public A zero() {
return def.empty();
}
/**
* Returns a value summed n
times (a + a + ... + a
).
* The default definition uses peasant multiplication, exploiting
* associativity to only require `O(log n)` uses of
* {@link #sum(Object, Object)}.
*
* @param n multiplier
* @param a the value to be reapeatly summed
* @return {@code a} summed {@code n} times. If {@code n <= 0}, returns
* {@code zero()}
*/
public A multiply(final int n, final A a) {
return def.multiply(n, a);
}
/**
* Sums the given values with right-fold.
*
* @param as The values to sum.
* @return The sum of the given values.
*/
public A sumRight(final List as) {
return as.foldRight(def::append, def.empty());
}
/**
* Sums the given values with right-fold.
*
* @param as The values to sum.
* @return The sum of the given values.
*/
public A sumRight(final Stream as) {
return as.foldRight1(def::append, def.empty());
}
/**
* Sums the given values with left-fold.
*
* @param as The values to sum.
* @return The sum of the given values.
*/
public A sumLeft(final List as) {
return as.foldLeft(def::append, def.empty());
}
/**
* Sums the given values with left-fold.
*
* @param as The values to sum.
* @return The sum of the given values.
*/
public A sumLeft(final Stream as) {
return def.sum(() -> as);
}
/**
* Returns a function that sums the given values with left-fold.
*
* @return a function that sums the given values with left-fold.
*/
public F, A> sumLeft() {
return this::sumLeft;
}
/**
* Returns a function that sums the given values with right-fold.
*
* @return a function that sums the given values with right-fold.
*/
public F, A> sumRight() {
return this::sumRight;
}
/**
* Returns a function that sums the given values with left-fold.
*
* @return a function that sums the given values with left-fold.
*/
public F, A> sumLeftS() {
return this::sumLeft;
}
/**
* Intersperses the given value between each two elements of the iterable, and sums the result.
*
* @param as An iterable of values to sum.
* @param a The value to intersperse between values of the given iterable.
* @return The sum of the given values and the interspersed value.
*/
public A join(final Iterable as, final A a) {
final Stream s = iterableStream(as);
F prependA = def.prepend(a);
return s.isEmpty()
? def.empty()
: s.foldLeft1((a1, a2) -> def.append(a1, prependA.f(a2)));
}
/**
* Swaps the arguments when summing.
*/
public Monoid dual() {
return monoidDef(def.dual());
}
/**
* Constructs a monoid from the given definition, which must follow the monoidal
* laws.
*
* @param def The definition for the monoid.
* @return A monoid instance that uses the given sun function and zero value.
*/
public static Monoid monoidDef(Definition def) {
return new Monoid<>(def);
}
/**
* Constructs a monoid from the given definition, which must follow the monoidal
* laws.
*
* @param def The definition for the monoid.
* @return A monoid instance that uses the given sun function and zero value.
*/
public static Monoid monoidDef(AltDefinition def) {
return new Monoid<>(def);
}
/**
* Constructs a monoid from the given semigroup definition and zero value, which must follow the monoidal laws.
*
* @param s The semigroup definition for the monoid.
* @param zero The zero for the monoid.
* @return A monoid instance that uses the given sun function and zero value.
*/
public static Monoid monoidDef(final Semigroup.Definition s, final A zero) {
return new Monoid<>(new Monoid.Definition() {
@Override
public A empty() {
return zero;
}
@Override
public A sum(F0> as) {
return s.sum(zero, as);
}
@Override
public A sum(A a, F0> as) {
return s.sum(a, as);
}
@Override
public A multiply(int n, A a) {
return (n <= 0)
? zero
: s.multiply1p(n - 1, a);
}
@Override
public A multiply1p(int n, A a) {
return s.multiply1p(n, a);
}
@Override
public A append(A a1, A a2) {
return s.append(a1, a2);
}
@Override
public F prepend(A a) {
return s.prepend(a);
}
});
}
/**
* Constructs a monoid from the given semigroup definition and zero value, which must follow the monoidal laws.
*
* @param s The semigroup definition for the monoid.
* @param zero The zero for the monoid.
* @return A monoid instance that uses the given sun function and zero value.
*/
public static Monoid monoidDef(final Semigroup.AltDefinition s, final A zero) {
return monoidDef((Semigroup.Definition) s, zero);
}
/**
* Constructs a monoid from the given sum function and zero value, which must follow the monoidal
* laws.
* Java 8+ users: use {@link #monoidDef(Semigroup.Definition, Object)} instead.
*
* @param sum The sum function for the monoid.
* @param zero The zero for the monoid.
* @return A monoid instance that uses the given sun function and zero value.
*/
public static Monoid monoid(final F> sum, final A zero) {
return new Monoid<>(new AltDefinition() {
@Override
public F prepend(A a) {
return sum.f(a);
}
@Override
public A empty() {
return zero;
}
});
}
/**
* Constructs a monoid from the given sum function and zero value, which must follow the monoidal
* laws.
*
* Java 8+ users: use {@link #monoidDef(Semigroup.Definition, Object)} instead.
*
* @param sum The sum function for the monoid.
* @param zero The zero for the monoid.
* @return A monoid instance that uses the given sun function and zero value.
*/
public static Monoid monoid(final F2 sum, final A zero) {
return new Monoid<>(new Definition() {
@Override
public A empty() {
return zero;
}
@Override
public A append(A a1, A a2) {
return sum.f(a1, a2);
}
});
}
/**
* Constructs a monoid from the given semigroup and zero value, which must follow the monoidal laws.
* @deprecated since 4.7. Use {@link #monoidDef(Semigroup.Definition, Object)} or {@link Semigroup#monoid(Object)} instead.
*
* @param s The semigroup for the monoid.
* @param zero The zero for the monoid.
* @return A monoid instance that uses the given sun function and zero value.
*/
@Deprecated
public static Monoid monoid(final Semigroup s, final A zero) {
return s.monoid(zero);
}
/**
* A monoid that adds integers.
*/
public static final Monoid intAdditionMonoid = monoidDef(new Definition() {
@Override
public Integer empty() {
return 0;
}
@Override
public Integer append(Integer a1, Integer a2) {
return a1 + a2;
}
@Override
public Integer multiply(int n, Integer i) {
return n <= 0 ? 0 : n * i;
}
});
/**
* A monoid that multiplies integers.
*/
public static final Monoid intMultiplicationMonoid = monoidDef(new Definition() {
@Override
public Integer empty() {
return 1;
}
@Override
public Integer append(Integer i1, Integer i2) {
return i1 * i2;
}
@Override
public Integer sum(F0> as) {
int x = 1;
for (Stream xs = as.f(); x != 0 && !xs.isEmpty(); xs = xs.tail()._1()) {
x *= xs.head();
}
return x;
}
@Override
public Integer multiply(int n, Integer integer) {
return n <= 0 ? 1 : (int) StrictMath.pow(integer.doubleValue(), n);
}
});
/**
* @deprecated Since 4.7. Due to rounding errors, addition of doubles does not comply with monoid laws
*/
@Deprecated
public static final Monoid doubleAdditionMonoid = monoidDef((d1, d2) -> d1 + d2, 0.0);
/**
* @deprecated Since 4.7. Due to rounding errors, multiplication of doubles does not comply with monoid laws
*/
@Deprecated
public static final Monoid doubleMultiplicationMonoid = monoidDef((d1, d2) -> d1 * d2, 1.0);
/**
* A monoid that adds big integers.
*/
public static final Monoid bigintAdditionMonoid = monoidDef(new Definition() {
@Override
public BigInteger empty() {
return BigInteger.ZERO;
}
@Override
public BigInteger append(BigInteger a1, BigInteger a2) {
return a1.add(a2);
}
@Override
public BigInteger multiply(int n, BigInteger a) {
return n <= 0 ? BigInteger.ZERO : a.multiply(BigInteger.valueOf(n));
}
});
/**
* A monoid that multiplies big integers.
*/
public static final Monoid bigintMultiplicationMonoid = monoidDef(new Definition() {
@Override
public BigInteger empty() {
return BigInteger.ONE;
}
@Override
public BigInteger append(BigInteger a1, BigInteger a2) {
return a1.multiply(a2);
}
@Override
public BigInteger multiply(int n, BigInteger a) {
return n <= 0 ? BigInteger.ONE : a.pow(n);
}
});
/**
* A monoid that adds big decimals.
*/
public static final Monoid bigdecimalAdditionMonoid =
monoidDef(new Definition() {
@Override
public BigDecimal empty() {
return BigDecimal.ZERO;
}
@Override
public BigDecimal append(BigDecimal a1, BigDecimal a2) {
return a1.add(a2);
}
@Override
public BigDecimal multiply(int n, BigDecimal a) {
return n <= 0 ? BigDecimal.ZERO : a.multiply(BigDecimal.valueOf(n));
}
});
/**
* A monoid that multiplies big decimals.
*/
public static final Monoid bigdecimalMultiplicationMonoid =
monoidDef(new Definition() {
@Override
public BigDecimal empty() {
return BigDecimal.ONE;
}
@Override
public BigDecimal append(BigDecimal a1, BigDecimal a2) {
return a1.multiply(a2);
}
@Override
public BigDecimal multiply(int n, BigDecimal decimal) {
return n <= 0 ? BigDecimal.ONE : decimal.pow(n);
}
});
/**
* A monoid that adds natural numbers.
*/
public static final Monoid naturalAdditionMonoid =
monoidDef(new Definition() {
@Override
public Natural empty() {
return Natural.ZERO;
}
@Override
public Natural append(Natural a1, Natural a2) {
return a1.add(a2);
}
@Override
public Natural multiply(int n, Natural a) {
return natural(n).map(positiveN -> a.multiply(positiveN)).orSome(Natural.ZERO);
}
});
/**
* A monoid that multiplies natural numbers.
*/
public static final Monoid naturalMultiplicationMonoid =
monoidDef(new Definition() {
@Override
public Natural empty() {
return Natural.ONE;
}
@Override
public Natural append(Natural a1, Natural a2) {
return a1.multiply(a2);
}
});
/**
* A monoid that adds longs.
*/
public static final Monoid longAdditionMonoid = monoidDef(new Definition() {
@Override
public Long empty() {
return 0L;
}
@Override
public Long append(Long a1, Long a2) {
return a1 + a2;
}
@Override
public Long multiply(int n, Long a) {
return n <= 0 ? 0L : n * a;
}
});
/**
* A monoid that multiplies longs.
*/
public static final Monoid longMultiplicationMonoid = monoidDef(new Definition() {
@Override
public Long empty() {
return 1L;
}
@Override
public Long append(Long i1, Long i2) {
return i1 * i2;
}
@Override
public Long sum(F0> as) {
long x = 1L;
for (Stream xs = as.f(); x != 0L && !xs.isEmpty(); xs = xs.tail()._1()) {
x *= xs.head();
}
return x;
}
@Override
public Long multiply(int n, Long l) {
return n <= 0 ? 1L : (long) StrictMath.pow(l.doubleValue(), n);
}
});
/**
* A monoid that ORs booleans.
*/
public static final Monoid disjunctionMonoid = monoidDef(new Definition() {
@Override
public Boolean empty() {
return false;
}
@Override
public Boolean append(Boolean a1, Boolean a2) {
return a1 | a2;
}
@Override
public Boolean sum(F0> as) {
return as.f().filter(identity()).isNotEmpty();
}
@Override
public Boolean multiply(int n, Boolean a) {
return n <= 0 ? false : a;
}
});
/**
* A monoid that XORs booleans.
*/
public static final Monoid exclusiveDisjunctionMonoid = monoidDef(new Definition() {
@Override
public Boolean empty() {
return false;
}
@Override
public Boolean append(Boolean a1, Boolean a2) {
return a1 ^ a2;
}
@Override
public Boolean multiply(int n, Boolean a) {
return a && (n == 1);
}
});
/**
* A monoid that ANDs booleans.
*/
public static final Monoid conjunctionMonoid = monoidDef(new Definition() {
@Override
public Boolean empty() {
return true;
}
@Override
public Boolean append(Boolean a1, Boolean a2) {
return a1 & a2;
}
@Override
public Boolean multiply(int n, Boolean a) {
return a;
}
@Override
public Boolean sum(F0> as) {
return as.f().filter(a -> !a).isEmpty();
}
});
/**
* A monoid that appends strings.
*/
public static final Monoid stringMonoid = monoidDef(new Definition() {
@Override
public String empty() {
return "";
}
@Override
public String append(String a1, String a2) {
return a1.concat(a2);
}
@Override
public String sum(F0> as) {
StringBuilder sb = new StringBuilder();
as.f().foreachDoEffect(sb::append);
return sb.toString();
}
});
/**
* A monoid that appends string buffers.
*/
public static final Monoid stringBufferMonoid = monoidDef((s1, s2) -> new StringBuffer(s1).append(s2), new StringBuffer(0));
/**
* A monoid that appends string builders.
*/
public static final Monoid stringBuilderMonoid = monoidDef((s1, s2) -> new StringBuilder(s1).append(s2), new StringBuilder(0));
/**
* A monoid for functions.
*
* @param mb The monoid for the function codomain.
* @return A monoid for functions.
*/
public static Monoid> functionMonoid(final Monoid mb) {
Definition mbDef = mb.def;
return monoidDef(new Definition>() {
@Override
public F empty() {
return __ -> mbDef.empty();
}
@Override
public F append(F a1, F a2) {
return a -> mbDef.append(a1.f(a), a2.f(a));
}
});
}
/**
* A monoid for lists.
*
* @return A monoid for lists.
*/
public static Monoid> listMonoid() {
return monoidDef(new Definition>() {
@Override
public List empty() {
return nil();
}
@Override
public List append(List a1, List a2) {
return a1.append(a2);
}
@Override
public List sum(F0>> as) {
return as.f().map(DList::listDList).foldLeft(DList::append, DList.nil()).run();
}
});
}
/**
* A monoid for options.
* @deprecated since 4.7. Use {@link #firstOptionMonoid()}.
*
* @return A monoid for options.
*/
@Deprecated
public static Monoid