All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fj.Monoid Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
package fj;

import static fj.Function.curry;
import static fj.Function.flip;
import fj.data.Array;
import fj.data.List;
import fj.data.IO;
import fj.data.IOFunctions;
import fj.data.Natural;
import fj.data.Option;
import fj.data.Set;
import fj.data.Stream;

import static fj.Function.flip;
import static fj.Semigroup.multiply1p;
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 F> sum; private final A zero; private Monoid(final F> sum, final A zero) { this.sum = sum; this.zero = zero; } /** * Composes this monoid with another. */ public Monoid>compose(Monoid m) { return monoid((P2 x) -> (P2 y) -> P.p(sum(x._1(), y._1()), m.sum(x._2(), y._2())), P.p(zero, m.zero)); } /** * Returns a semigroup projection of this monoid. * * @return A semigroup projection of this monoid. */ public Semigroup semigroup() { return Semigroup.semigroup(sum); } /** * 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 sum.f(a1).f(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 sum.f(a1); } /** * Returns a function that sums according to this monoid. * * @return A function that sums according to this monoid. */ public F> sum() { return sum; } /** * The zero value for this monoid. * * @return The zero value for this monoid. */ public A zero() { return zero; } /** * 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 (n <= 0) ? zero : multiply1p(sum, n - 1, 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(sum, zero); } /** * 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.foldRight((a, ap1) -> sum(a, ap1._1()), zero); } /** * 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(sum, zero); } /** * 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 as.foldLeft(sum, zero); } /** * 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); return s.isEmpty() ? zero : s.foldLeft1(Function.compose(sum, flip(sum).f(a))); } /** * Swaps the arguments when summing. */ public Monoid dual() { return monoid(flip(sum), zero); } /** * Constructs a monoid from the given sum function and zero value, which must follow the monoidal * laws. * * @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<>(sum, zero); } /** * Constructs a monoid from the given sum function and zero value, which must follow the monoidal * laws. * * @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<>(curry(sum), zero); } /** * Constructs a monoid from the given semigroup and zero value, which must follow the monoidal laws. * * @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. */ public static Monoid monoid(final Semigroup s, final A zero) { return new Monoid<>(s.sum(), zero); } /** * A monoid that adds integers. */ public static final Monoid intAdditionMonoid = monoid(Semigroup.intAdditionSemigroup, 0); /** * A monoid that multiplies integers. */ public static final Monoid intMultiplicationMonoid = monoid(Semigroup.intMultiplicationSemigroup, 1); /** * A monoid that adds doubles. */ public static final Monoid doubleAdditionMonoid = monoid(Semigroup.doubleAdditionSemigroup, 0.0); /** * A monoid that multiplies doubles. */ public static final Monoid doubleMultiplicationMonoid = monoid(Semigroup.doubleMultiplicationSemigroup, 1.0); /** * A monoid that adds big integers. */ public static final Monoid bigintAdditionMonoid = monoid(Semigroup.bigintAdditionSemigroup, BigInteger.ZERO); /** * A monoid that multiplies big integers. */ public static final Monoid bigintMultiplicationMonoid = monoid(Semigroup.bigintMultiplicationSemigroup, BigInteger.ONE); /** * A monoid that adds big decimals. */ public static final Monoid bigdecimalAdditionMonoid = monoid(Semigroup.bigdecimalAdditionSemigroup, BigDecimal.ZERO); /** * A monoid that multiplies big decimals. */ public static final Monoid bigdecimalMultiplicationMonoid = monoid(Semigroup.bigdecimalMultiplicationSemigroup, BigDecimal.ONE); /** * A monoid that adds natural numbers. */ public static final Monoid naturalAdditionMonoid = monoid(Semigroup.naturalAdditionSemigroup, Natural.ZERO); /** * A monoid that multiplies natural numbers. */ public static final Monoid naturalMultiplicationMonoid = monoid(Semigroup.naturalMultiplicationSemigroup, Natural.ONE); /** * A monoid that adds longs. */ public static final Monoid longAdditionMonoid = monoid(Semigroup.longAdditionSemigroup, 0L); /** * A monoid that multiplies longs. */ public static final Monoid longMultiplicationMonoid = monoid(Semigroup.longMultiplicationSemigroup, 1L); /** * A monoid that ORs booleans. */ public static final Monoid disjunctionMonoid = monoid(Semigroup.disjunctionSemigroup, false); /** * A monoid that XORs booleans. */ public static final Monoid exclusiveDisjunctionMonoid = monoid(Semigroup.exclusiveDisjunctionSemiGroup, false); /** * A monoid that ANDs booleans. */ public static final Monoid conjunctionMonoid = monoid(Semigroup.conjunctionSemigroup, true); /** * A monoid that appends strings. */ public static final Monoid stringMonoid = monoid(Semigroup.stringSemigroup, ""); /** * A monoid that appends string buffers. */ public static final Monoid stringBufferMonoid = monoid(Semigroup.stringBufferSemigroup, new StringBuffer()); /** * A monoid that appends string builders. */ public static final Monoid stringBuilderMonoid = monoid(Semigroup.stringBuilderSemigroup, new StringBuilder()); /** * A monoid for functions. * * @param mb The monoid for the function codomain. * @return A monoid for functions. */ public static Monoid> functionMonoid(final Monoid mb) { return monoid(Semigroup.functionSemigroup(mb.semigroup()), Function.constant(mb.zero)); } /** * A monoid for lists. * * @return A monoid for lists. */ public static Monoid> listMonoid() { return monoid(Semigroup.listSemigroup(), List.nil()); } /** * A monoid for options. * * @return A monoid for options. */ public static Monoid> optionMonoid() { return monoid(Semigroup.optionSemigroup(), Option.none()); } /** * A monoid for options that take the first available value. * * @return A monoid for options that take the first available value. */ public static Monoid> firstOptionMonoid() { return monoid(Semigroup.firstOptionSemigroup(), Option.none()); } /** * A monoid for options that take the last available value. * * @return A monoid for options that take the last available value. */ public static Monoid> lastOptionMonoid() { return monoid(Semigroup.lastOptionSemigroup(), Option.none()); } /** * A monoid for streams. * * @return A monoid for streams. */ public static Monoid> streamMonoid() { return monoid(Semigroup.streamSemigroup(), Stream.nil()); } /** * A monoid for arrays. * * @return A monoid for arrays. */ @SuppressWarnings("unchecked") public static Monoid> arrayMonoid() { return monoid(Semigroup.arraySemigroup(), Array.empty()); } /** * A monoid for IO values. */ public static Monoid> ioMonoid(final Monoid ma) { return monoid(Semigroup.ioSemigroup(ma.semigroup()), IOFunctions.unit(ma.zero())); } /** * A monoid for the maximum of two integers. */ public static final Monoid intMaxMonoid = monoid(Semigroup.intMaximumSemigroup, Integer.MIN_VALUE); /** * A monoid for the minimum of two integers. */ public static final Monoid intMinMonoid = monoid(Semigroup.intMinimumSemigroup, Integer.MAX_VALUE); /** * A monoid for the Unit value. */ public static final Monoid unitMonoid = monoid(Semigroup.unitSemigroup, Unit.unit()); /** * A monoid for sets. * * @param o An order for set elements. * @return A monoid for sets whose elements have the given order. */ public static Monoid> setMonoid(final Ord o) { return monoid(Semigroup.setSemigroup(), Set.empty(o)); } /** * A monoid for the maximum of elements with ordering o. * * @param o An ordering of elements. * @param zero The minimum element. */ public static Monoid ordMaxMonoid(final Ord o, final A zero) { return monoid(o.max, zero); } }