package fj;
import static fj.Function.curry;
import static fj.Function.compose;
import static fj.Function.flip;
import fj.data.Array;
import fj.data.List;
import fj.data.Natural;
import fj.data.Option;
import fj.data.Set;
import fj.data.Stream;
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. forall y. forall 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;
}
/**
* 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;
}
/**
* 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(new F2 , A>() {
public A f(final A a, final P1 ap1) {
return 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 new F, A>() {
public A f(final List as) {
return sumLeft(as);
}
};
}
/**
* 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 new F, A>() {
public A f(final List as) {
return sumRight(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> sumLeftS() {
return new F, A>() {
public A f(final Stream as) {
return sumLeft(as);
}
};
}
/**
* 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(compose(sum, flip(sum).f(a)));
}
/**
* 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 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));
}
}