
fj.Monoid Maven / Gradle / Ivy
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.Option.some;
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 {@code 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();
}
});
}
/**
* Lift a {@code Semigroup} for A to a {@code Monoid
© 2015 - 2025 Weber Informatics LLC | Privacy Policy