javaslang.algebra.Monoid Maven / Gradle / Ivy
/* / \____ _ _ ____ ______ / \ ____ __ _______
* / / \/ \ / \/ \ / /\__\/ // \/ \ // /\__\ JΛVΛSLΛNG
* _/ / /\ \ \/ / /\ \\__\\ \ // /\ \ /\\/ \ /__\ \ Copyright 2014-2017 Javaslang, http://javaslang.io
* /___/\_/ \_/\____/\_/ \_/\__\/__/\__\_/ \_// \__/\_____/ Licensed under the Apache License, Version 2.0
*/
package javaslang.algebra;
import javaslang.collection.Foldable;
import java.util.Objects;
import java.util.function.Function;
/**
* A Monoid is a {@linkplain javaslang.algebra.Semigroup} (types with an associative binary operation) that has an
* identity element {@code zero}.
* Given a type {@code A}, instances of Monoid should satisfy the following laws:
*
* - Associativity: {@code combine(combine(x,y),z) == combine(x,combine(y,z))} for any {@code x,y,z} of type
* {@code A}.
* - Identity: {@code combine(zero(), x) == x == combine(x, zero())} for any {@code x} of type {@code A}.
*
* Example: {@linkplain java.lang.String} is a Monoid with zero {@code ""} (empty String) and String concatenation
* {@code +} as combine operation.
* Please note that some types can be viewed as a monoid in more than one way, e.g. both addition and multiplication
* on numbers.
*
* Folding:
*
*
* - {@link #fold(Monoid, Foldable)}
* - {@link #foldLeft(Monoid, Foldable)}
* - {@link #foldMap(Monoid, Foldable, Function)}
* - {@link #foldRight(Monoid, Foldable)}
*
*
* @param A type.
* @author Daniel Dietrich
* @since 1.1.0
*/
public interface Monoid extends Semigroup {
/**
* Factory method for monoids, taking a zero and a Semigroup.
*
* @param Value type
* @param zero The zero of the Monoid.
* @param semigroup The associative binary operation of the Monoid. Please note that
* {@linkplain javaslang.algebra.Semigroup} is a {@linkplain java.lang.FunctionalInterface}.
* @return a new Monoid on type A
* @throws NullPointerException if {@code semigroup} is null
*/
static Monoid of(A zero, Semigroup semigroup) {
Objects.requireNonNull(semigroup, "semigroup is null");
return new Monoid() {
@Override
public A combine(A a1, A a2) {
return semigroup.combine(a1, a2);
}
@Override
public A zero() {
return zero;
}
};
}
/**
* The monoid of endomorphisms under composition.
*
* @param Value type
* @return The monoid of endomorphisms of type A.
*/
static Monoid> endoMonoid() {
return Monoid.of(Function.identity(), Function::compose);
}
/**
* The unique neutral element regarding {@linkplain #combine(Object, Object)}.
*
* @return The zero element of this Monoid
*/
A zero();
// -- Fold operations
/**
* Folds the elements of {@code Foldable} from the left, starting with {@code monoid.zero()}
* and successively calling {@code monoid::combine}.
*
* @param monoid A monoid, providing a {@code zero} and a {@code combine} function.
* @param foldable A foldable
* @param type of the foldable elements
* @return a folded value
* @throws NullPointerException if {@code monoid} or {@code foldable} is null
*/
static T fold(Monoid monoid, Foldable foldable) {
Objects.requireNonNull(monoid, "monoid is null");
Objects.requireNonNull(foldable, "foldable is null");
return foldable.foldLeft(monoid.zero(), monoid::combine);
}
/**
* Folds the elements of {@code Foldable} from the left, starting with {@code monoid.zero()}
* and successively calling {@code monoid::combine}.
*
* @param monoid A monoid, providing a {@code zero} and a {@code combine} function.
* @param foldable A foldable
* @param type of the foldable elements
* @return a folded value
* @throws NullPointerException if {@code monoid} is null
*/
static T foldLeft(Monoid monoid, Foldable foldable) {
Objects.requireNonNull(monoid, "monoid is null");
Objects.requireNonNull(foldable, "foldable is null");
return foldable.foldLeft(monoid.zero(), monoid::combine);
}
/**
* Maps this elements to a {@code Monoid} and applies {@code foldLeft}, starting with {@code monoid.zero()}:
*
* foldLeft(monoid.zero(), (ys, x) -> monoid.combine(ys, mapper.apply(x)));
*
*
* @param monoid A Monoid
* @param foldable A foldable
* @param mapper A mapper
* @param type of the foldable elements
* @param Component type of the given monoid.
* @return the folded monoid value.
* @throws NullPointerException if {@code monoid} or {@code mapper} is null
*/
static U foldMap(Monoid monoid, Foldable foldable, Function super T, ? extends U> mapper) {
Objects.requireNonNull(monoid, "monoid is null");
Objects.requireNonNull(foldable, "foldable is null");
Objects.requireNonNull(mapper, "mapper is null");
return foldable.foldLeft(monoid.zero(), (ys, x) -> monoid.combine(ys, mapper.apply(x)));
}
/**
* Folds this elements from the right, starting with {@code monoid.zero()} and successively calling {@code monoid::combine}.
*
* @param monoid A monoid, providing a {@code zero} and a {@code combine} function.
* @param foldable A foldable
* @param type of the foldable elements
* @return a folded value
* @throws NullPointerException if {@code monoid} is null
*/
static T foldRight(Monoid monoid, Foldable foldable) {
Objects.requireNonNull(monoid, "monoid is null");
Objects.requireNonNull(foldable, "foldable is null");
return foldable.foldRight(monoid.zero(), monoid::combine);
}
}