package fj; import fj.data.Array; import fj.data.DList; import fj.data.List; import fj.data.IO; import fj.data.Natural; import fj.data.NonEmptyList; import fj.data.Option; import fj.data.Set; import fj.data.Stream; import java.math.BigDecimal; import java.math.BigInteger; import static fj.F1Functions.dimap; import static fj.Function.constant; import static fj.Function.identity; import static fj.Monoid.*; import static fj.data.DList.listDList; import static fj.data.Option.none; import static fj.data.Option.some; /** * Implementations must satisfy the law of associativity: * <ul> * <li><em>Associativity</em>; forall x. forall y. forall z. sum(sum(x, y), z) == sum(x, sum(y, z))</li> * </ul> * * @version %build.number% */ public final class Semigroup<A> { /** * Primitives functions of Semigroup: minimal definition and overridable methods. */ public interface Definition<A> { A append(A a1, A a2); default F<A, A> prepend(A a) { return a2 -> append(a, a2); } default A sum(A a, F0<Stream<A>> as) { return as.f().foldLeft(this::append, a); } default A multiply1p(int n, A a) { if (n <= 0) { return a; } A xTmp = a; int yTmp = n; A zTmp = a; while (true) { if ((yTmp & 1) == 1) { zTmp = append(xTmp, zTmp); if (yTmp == 1) { return zTmp; } } xTmp = append(xTmp, xTmp); yTmp = (yTmp) >>> 1; } } default Definition<A> dual() { return new Definition<A>(){ @Override public A append(A a1, A a2) { return Definition.this.append(a2, a1); } @Override public A multiply1p(int n, A a) { return Definition.this.multiply1p(n, a); } }; } } /** * Primitives functions of Semigroup: alternative minimal definition and overridable methods. */ public interface AltDefinition<A> extends Definition<A> { @Override F<A, A> prepend(A a); @Override default A append(A a1, A a2) { return prepend(a1).f(a2); } } private final Definition<A> def; private Semigroup(final Definition<A> def) { this.def = def; } /** * 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 semigroup. * * @param a1 The value to sum. * @return A function that sums the given value according to this semigroup. */ public F<A, A> sum(final A a1) { return def.prepend(a1); } /** * Returns a function that sums according to this semigroup. * * @return A function that sums according to this semigroup. */ public F<A, F<A, A>> sum() { return def::prepend; } /** * Returns a value summed <code>n + 1</code> times ( * <code>a + a + ... + a</code>) 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 n + 1 times * @return {@code a} summed {@code n} times. If {@code n <= 0}, returns * {@code zero()} */ public A multiply1p(int n, A a) { return def.multiply1p(n, a); } /** * Sums the given values with left-fold. */ public A sumNel(final NonEmptyList<A> as) { return as.foldLeft1(def::append); } /** * Sums the given values with left-fold, shortcutting the computation as early as possible. */ public A sumStream(A a, F0<Stream<A>> as) { return def.sum(a, as); } /** * Swaps the arguments when summing. */ public Semigroup<A> dual() { return semigroupDef(def.dual()); } /** * Lifts the semigroup to obtain a trivial monoid. */ public Monoid<Option<A>> lift() { Definition<A> def = this.def; return monoidDef(new Monoid.Definition<Option<A>>() { @Override public Option<A> empty() { return none(); } @Override public Option<A> append(Option<A> a1, Option<A> a2) { return a1.liftM2(a1, def::append).orElse(a1).orElse(a2); } @Override public Option<A> multiply(int n, Option<A> oa) { return n > 0 ? oa.map(a -> def.multiply1p(n - 1, a)) : none(); } @Override public Option<A> sum(F0<Stream<Option<A>>> oas) { Stream<A> as = oas.f().bind(Option::toStream); return as.uncons(none(), h -> tail -> some(def.sum(h, tail::_1))); } }); } /** * 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 <B> Semigroup<B> xmap(final F<A, B> f, final F<B, A> g) { Definition<A> def = this.def; return semigroupDef(new Definition<B>() { @Override public B append(B a1, B a2) { return f.f(def.append(g.f(a1), g.f(a2))); } @Override public F<B, B> prepend(B b) { return dimap(def.prepend(g.f(b)), g, f); } @Override public B multiply1p(int n, B b) { return f.f(def.multiply1p(n , g.f(b))); } @Override public B sum(B b, F0<Stream<B>> bs) { return f.f(def.sum(g.f(b), () -> bs.f().map(g))); } }); } public <B, C> Semigroup<C> compose(Semigroup<B> sb, final F<C, B> b, final F<C, A> a, final F2<A, B, C> c) { Definition<A> saDef = this.def; Definition<B> sbDef = sb.def; return semigroupDef(new Definition<C>() { @Override public C append(C c1, C c2) { return c.f(saDef.append(a.f(c1), a.f(c2)), sbDef.append(b.f(c1), b.f(c2))); } @Override public F<C, C> prepend(C c1) { F<A, A> prependA = saDef.prepend(a.f(c1)); F<B, B> prependB = sbDef.prepend(b.f(c1)); return c2 -> c.f(prependA.f(a.f(c2)), prependB.f(b.f(c2))); } @Override public C multiply1p(int n, C c1) { return c.f(saDef.multiply1p(n, a.f(c1)), sbDef.multiply1p(n, b.f(c1))); } @Override public C sum(C c1, F0<Stream<C>> cs) { return c.f(saDef.sum(a.f(c1), () -> cs.f().map(a)), sbDef.sum(b.f(c1), () -> cs.f().map(b))); } }); } /** * Constructs a monoid from this semigroup and a zero value, which must follow the monoidal laws. * * @param zero The zero for the monoid. * @return A monoid instance that uses the given sun function and zero value. */ public Monoid<A> monoid(A zero) { return monoidDef(this.def, zero); } /** * Constructs a semigroup from the given definition. * * @param def The definition to construct this semigroup with. * @return A semigroup from the given definition. */ public static <A> Semigroup<A> semigroupDef(final Definition<A> def) { return new Semigroup<>(def); } /** * Constructs a semigroup from the given definition. * * @param def The definition to construct this semigroup with. * @return A semigroup from the given definition. */ public static <A> Semigroup<A> semigroupDef(final AltDefinition<A> def) { return new Semigroup<>(def); } /** * Constructs a semigroup from the given function. * Java 8+ users: use {@link #semigroupDef(AltDefinition)} instead. * * @param sum The function to construct this semigroup with. * @return A semigroup from the given function. */ public static <A> Semigroup<A> semigroup(final F<A, F<A, A>> sum) { return semigroupDef(sum::f); } /** * Constructs a semigroup from the given function. * Java 8+ users: use {@link #semigroupDef(Definition)} instead. * * @param sum The function to construct this semigroup with. * @return A semigroup from the given function. */ public static <A> Semigroup<A> semigroup(final F2<A, A, A> sum) { return new Semigroup<>(sum::f); } /** * A semigroup that adds integers. */ public static final Semigroup<Integer> intAdditionSemigroup = intAdditionMonoid.semigroup(); /** * @deprecated Since 4.7. Due to rounding errors, addition of doubles does not comply with monoid laws */ @Deprecated public static final Semigroup<Double> doubleAdditionSemigroup = semigroupDef((d1, d2) -> d1 + d2); /** * A semigroup that multiplies integers. */ public static final Semigroup<Integer> intMultiplicationSemigroup = intMultiplicationMonoid.semigroup(); /** * @deprecated Since 4.7. Due to rounding errors, addition of doubles does not comply with monoid laws */ @Deprecated public static final Semigroup<Double> doubleMultiplicationSemigroup = semigroupDef((d1, d2) -> d1 * d2); /** * A semigroup that yields the maximum of integers. */ public static final Semigroup<Integer> intMaximumSemigroup = intMaxMonoid.semigroup(); /** * A semigroup that yields the minimum of integers. */ public static final Semigroup<Integer> intMinimumSemigroup = intMinMonoid.semigroup(); /** * A semigroup that adds big integers. */ public static final Semigroup<BigInteger> bigintAdditionSemigroup = bigintAdditionMonoid.semigroup(); /** * A semigroup that multiplies big integers. */ public static final Semigroup<BigInteger> bigintMultiplicationSemigroup = semigroup(BigInteger::multiply); /** * A semigroup that yields the maximum of big integers. */ public static final Semigroup<BigInteger> bigintMaximumSemigroup = Ord.bigintOrd.maxSemigroup(); /** * A semigroup that yields the minimum of big integers. */ public static final Semigroup<BigInteger> bigintMinimumSemigroup = Ord.bigintOrd.minSemigroup(); /** * A semigroup that adds big decimals. */ public static final Semigroup<BigDecimal> bigdecimalAdditionSemigroup = bigdecimalAdditionMonoid.semigroup(); /** * A semigroup that multiplies big decimals. */ public static final Semigroup<BigDecimal> bigdecimalMultiplicationSemigroup = bigdecimalMultiplicationMonoid.semigroup(); /** * A semigroup that yields the maximum of big decimals. */ public static final Semigroup<BigDecimal> bigDecimalMaximumSemigroup = Ord.bigdecimalOrd.maxSemigroup(); /** * A semigroup that yields the minimum of big decimals. */ public static final Semigroup<BigDecimal> bigDecimalMinimumSemigroup = Ord.bigdecimalOrd.minSemigroup(); /** * A semigroup that multiplies natural numbers. */ public static final Semigroup<Natural> naturalMultiplicationSemigroup = naturalMultiplicationMonoid.semigroup(); /** * A semigroup that adds natural numbers. */ public static final Semigroup<Natural> naturalAdditionSemigroup = naturalAdditionMonoid.semigroup(); /** * A semigroup that yields the maximum of natural numbers. */ public static final Semigroup<Natural> naturalMaximumSemigroup = Ord.naturalOrd.maxSemigroup(); /** * A semigroup that yields the minimum of natural numbers. */ public static final Semigroup<Natural> naturalMinimumSemigroup = Ord.naturalOrd.minSemigroup(); /** * A semigroup that adds longs. */ public static final Semigroup<Long> longAdditionSemigroup = longAdditionMonoid.semigroup(); /** * A semigroup that multiplies longs. */ public static final Semigroup<Long> longMultiplicationSemigroup = longMultiplicationMonoid.semigroup(); /** * A semigroup that yields the maximum of longs. */ public static final Semigroup<Long> longMaximumSemigroup = Ord.longOrd.maxSemigroup(); /** * A semigroup that yields the minimum of longs. */ public static final Semigroup<Long> longMinimumSemigroup = Ord.longOrd.minSemigroup(); /** * A semigroup that ORs booleans. */ public static final Semigroup<Boolean> disjunctionSemigroup = disjunctionMonoid.semigroup(); /** * A semigroup that XORs booleans. */ public static final Semigroup<Boolean> exclusiveDisjunctionSemiGroup = exclusiveDisjunctionMonoid.semigroup(); /** * A semigroup that ANDs booleans. */ public static final Semigroup<Boolean> conjunctionSemigroup = conjunctionMonoid.semigroup(); /** * A semigroup that appends strings. */ public static final Semigroup<String> stringSemigroup = stringMonoid.semigroup(); /** * A semigroup that appends string buffers. */ public static final Semigroup<StringBuffer> stringBufferSemigroup = stringBufferMonoid.semigroup(); /** * A semigroup that appends string builders. */ public static final Semigroup<StringBuilder> stringBuilderSemigroup = stringBuilderMonoid.semigroup(); /** * A semigroup which always uses the "first" (left-hand side) value. */ public static <A> Semigroup<A> firstSemigroup() { return semigroupDef(new Definition<A>() { @Override public A append(A a1, A a2) { return a1; } @Override public F<A, A> prepend(A a) { return constant(a); } @Override public A multiply1p(int n, A a) { return a; } @Override public A sum(A a, F0<Stream<A>> as) { return a; } }); } /** * A semigroup which always uses the "last" (right-hand side) value. */ public static <A> Semigroup<A> lastSemigroup() { return semigroupDef(new Definition<A>() { @Override public A append(A a1, A a2) { return a2; } @Override public F<A, A> prepend(A a) { return identity(); } @Override public A multiply1p(int n, A a) { return a; } }); } /** * A semigroup for functions. * * @param sb The smeigroup for the codomain. * @return A semigroup for functions. */ public static <A, B> Semigroup<F<A, B>> functionSemigroup(final Semigroup<B> sb) { Definition<B> sbDef = sb.def; return semigroupDef((a1, a2) -> a -> sbDef.append(a1.f(a), a2.f(a))); } /** * A semigroup for lists. * * @return A semigroup for lists. */ public static <A> Semigroup<List<A>> listSemigroup() { return Monoid.<A>listMonoid().semigroup(); } /** * A semigroup for non-empty lists. * * @return A semigroup for non-empty lists. */ public static <A> Semigroup<NonEmptyList<A>> nonEmptyListSemigroup() { return semigroupDef(new Definition<NonEmptyList<A>>() { @Override public NonEmptyList<A> append(NonEmptyList<A> a1, NonEmptyList<A> a2) { return a1.append(a2); } @Override public NonEmptyList<A> sum(NonEmptyList<A> nea, F0<Stream<NonEmptyList<A>>> neas) { List<A> tail = neas.f().map(nel -> listDList(nel.toList())).foldLeft(DList::append, DList.<A>nil()).run(); return nea.append(tail); } }); } /** * A semigroup for optional values. * @deprecated since 4.7. Use {@link #firstOptionSemigroup()}. * ** @return A semigroup for optional values. */ public static <A> Semigroup<Option<A>> optionSemigroup() { return firstOptionSemigroup(); } /** * A semigroup for optional values that take the first available value. * * @return A semigroup for optional values that take the first available value. */ public static <A> Semigroup<Option<A>> firstOptionSemigroup() { return Monoid.<A>firstOptionMonoid().semigroup(); } /** * A semigroup for optional values that take the last available value. * * @return A semigroup for optional values that take the last available value. */ public static <A> Semigroup<Option<A>> lastOptionSemigroup() { return Monoid.<A>lastOptionMonoid().semigroup(); } /** * A semigroup for streams. * * @return A semigroup for streams. */ public static <A> Semigroup<Stream<A>> streamSemigroup() { return Monoid.<A>streamMonoid().semigroup(); } /** * A semigroup for arrays. * * @return A semigroup for arrays. */ public static <A> Semigroup<Array<A>> arraySemigroup() { return Monoid.<A>arrayMonoid().semigroup(); } /** * A lazy semigroup for unary products. * * @param sa A semigroup for the product's type. * @return A semigroup for unary products. */ public static <A> Semigroup<P1<A>> p1Semigroup(final Semigroup<A> sa) { Definition<A> def = sa.def; return semigroupDef(new Definition<P1<A>>() { @Override public P1<A> append(P1<A> a1, P1<A> a2) { return P.lazy(() -> def.append(a1._1(), a2._1())); } @Override public P1<A> multiply1p(int n, P1<A> ap1) { return P.lazy(() -> def.multiply1p(n, ap1._1())); } @Override public P1<A> sum(P1<A> ap1, F0<Stream<P1<A>>> as) { return P.lazy(() -> def.sum(ap1._1(), () -> as.f().map(P1.__1()))); } }); } /** * A lazy semigroup for binary products. * * @param sa A semigroup for the product's first type. * @param sb A semigroup for the product's second type. * @return A semigroup for binary products. */ public static <A, B> Semigroup<P2<A, B>> p2Semigroup(final Semigroup<A> sa, final Semigroup<B> sb) { return semigroupDef((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()), () -> sb.sum(a1._2(), a2._2()))); } /** * A semigroup for IO values. */ public static <A> Semigroup<IO<A>> ioSemigroup(final Semigroup <A> sa) { Definition<A> def = sa.def; return semigroupDef((a1, a2) -> () -> def.append(a1.run(), a2.run())); } /** * A semigroup for the Unit value. */ public static final Semigroup<Unit> unitSemigroup = unitMonoid.semigroup(); /** * A semigroup for sets. * * @return a semigroup for sets. */ public static <A> Semigroup<Set<A>> setSemigroup() { return semigroupDef(Set::union); } }