1 /*
2 Copyright 2015 Atlassian
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 package io.atlassian.fugue;
18
19 import static io.atlassian.fugue.Iterables.concat;
20 import static io.atlassian.fugue.Pair.pair;
21 import static java.util.Collections.singletonList;
22
23 /**
24 * A Monoid is an algebraic structure consisting of an associative binary
25 * operation across the values of a given type (a monoid is a {@link Semigroup})
26 * and an identity element for this operation. Implementations must follow the
27 * monoidal laws:
28 * <ul>
29 * <li><em>Left Identity</em>; forall x. append(zero(), x) == x</li>
30 * <li><em>Right Identity</em>; forall x. append(x, zero()) == x</li>
31 * <li><em>Associativity</em>; forall x y z. append(append(x, y), z) ==
32 * append(x, append(y, z))</li>
33 * </ul>
34 * Methods {@link #sum(Iterable)} and {@link #multiply(int, Object)} can be
35 * overriden for performance reason, especially if {@link #sum(Iterable)} can be
36 * implemented to not require evaluation of the whole iterable. All other
37 * default methods should not be overriden.
38 *
39 * @see Semigroup
40 * @since 3.1
41 */
42 public interface Monoid<A> extends Semigroup<A> {
43
44 /**
45 * The identity element value for this monoid.
46 *
47 * @return The identity element for this monoid.
48 */
49 A zero();
50
51 /**
52 * Sums the given values.
53 *
54 * @param as The values to sum.
55 * @return The sum of the given values.
56 */
57 default A sum(final Iterable<A> as) {
58 return Semigroup.super.sumNonEmpty(zero(), as);
59 }
60
61 /**
62 * Returns a value summed <code>n</code> times (<code>a + a + ... + a</code>).
63 * The default definition uses peasant multiplication, exploiting
64 * associativity to only require `O(log n)` uses of
65 * {@link #append(Object, Object)}.
66 *
67 * @param n multiplier
68 * @param a the value to be reapeatly summed
69 * @return {@code a} summed {@code n} times. If {@code n <= 0}, returns
70 * {@code zero()}
71 */
72 default A multiply(final int n, final A a) {
73 return (n <= 0) ? zero() : Semigroup.super.multiply1p(n - 1, a);
74 }
75
76 // Derived methods: should not be overriden:
77
78 /**
79 * Intersperses the given value between each two elements of the collection,
80 * and sums the result.
81 *
82 * @param as An iterable of values.
83 * @param a The value to intersperse between values of the given iterable.
84 * @return The sum of the given values and the interspersed value.
85 */
86 default A intersperse(final Iterable<? extends A> as, final A a) {
87 return sum(Iterables.intersperse(as, a));
88 }
89
90 @Override default A sumNonEmpty(A head, Iterable<A> tail) {
91 return sum(concat(singletonList(head), tail));
92 }
93
94 @Override default A multiply1p(int n, A a) {
95 return n == Integer.MAX_VALUE ? append(a, multiply(n, a)) : multiply(n + 1, a);
96 }
97
98 /**
99 * Composes a monoid with another.
100 */
101 public static <A, B> Monoid<Pair<A, B>> compose(Monoid<A> ma, Monoid<B> mb) {
102 Pair<A, B> zero = pair(ma.zero(), mb.zero());
103 return new Monoid<Pair<A, B>>() {
104 @Override public Pair<A, B> append(Pair<A, B> p1, Pair<A, B> p2) {
105 return pair(ma.append(p1.left(), p2.left()), mb.append(p1.right(), p2.right()));
106 }
107
108 @Override public Pair<A, B> zero() {
109 return zero;
110 }
111 };
112 }
113
114 /**
115 * Return the dual Monoid.
116 *
117 * @param monoid a monoid.
118 * @return a Monoid appending in reverse order,
119 */
120 public static <A> Monoid<A> dual(Monoid<A> monoid) {
121 return new Monoid<A>() {
122 @Override public A append(A a1, A a2) {
123 return monoid.append(a2, a1);
124 }
125
126 @Override public A zero() {
127 return monoid.zero();
128 }
129
130 @Override public A multiply(int n, A a) {
131 return monoid.multiply(n, a);
132 }
133 };
134 }
135 }