View Javadoc

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.law;
18  
19  import io.atlassian.fugue.Functions;
20  import io.atlassian.fugue.Iterables;
21  import io.atlassian.fugue.Monoid;
22  
23  import java.util.function.BiFunction;
24  
25  /**
26   * Laws for a monoid
27   *
28   */
29  public final class MonoidLaws<A> {
30  
31    private final Monoid<A> monoid;
32  
33    /**
34     * Build a law instance to check monoid properties
35     *
36     * @param monoid a {@link io.atlassian.fugue.Monoid} to check matches the
37     * desired behaviors of
38     */
39    public MonoidLaws(final Monoid<A> monoid) {
40      this.monoid = monoid;
41    }
42  
43    /**
44     * A monoid must not care about the order elements are combined. If you
45     * combine x with y and then with z the result should be the same as combining
46     * y with z and then with x.
47     *
48     * @param x an A object
49     * @param y an A object
50     * @param z an A object
51     * @return a {@link io.atlassian.fugue.law.IsEq} instance where
52     * append(append(x,y),z) is equal to append(x, append(y,z))
53     */
54    public IsEq<A> semigroupAssociative(final A x, final A y, final A z) {
55      return IsEq.isEq(monoid.append(monoid.append(x, y), z), monoid.append(x, monoid.append(y, z)));
56    }
57  
58    /**
59     * If the zero of your monoid is combine with an element of the type your
60     * monoid works with the result should be that element.
61     *
62     * @param x an element of your monoid type
63     * @return a {@link io.atlassian.fugue.law.IsEq} where x is equal to
64     * append(zero(), x)
65     */
66    public IsEq<A> monoidLeftIdentity(final A x) {
67      return IsEq.isEq(x, monoid.append(monoid.zero(), x));
68    }
69  
70    /**
71     * If an element of the type your monoid works with is combined with the zero
72     * of your monoid result should be that element.
73     *
74     * @param x an element of your monoid type
75     * @return a {@link io.atlassian.fugue.law.IsEq} where x is equal to append(x,
76     * zero())
77     */
78    public IsEq<A> monoidRightIdentity(final A x) {
79      return IsEq.isEq(x, monoid.append(x, monoid.zero()));
80    }
81  
82    /**
83     * The sum function of your monoid must be equal to
84     * {@link Functions#fold(BiFunction, Object, Iterable)} using append as the
85     * folding function and zero() as the initial value
86     *
87     * @param as a {@link java.lang.Iterable}
88     * @return a {@link io.atlassian.fugue.law.IsEq} where sum(as) is equal to
89     * fold(append, zero, as)
90     */
91    public IsEq<A> sumEqualFold(final Iterable<A> as) {
92      return IsEq.isEq(monoid.sum(as), Functions.fold(monoid::append, monoid.zero(), as));
93    }
94  
95    /**
96     * The multiply function of your monoid must be equal to the sum function
97     * called with an iterable containing {@code n} copies of the input type.
98     *
99     * @param n a int
100    * @param a an A
101    * @return a {@link io.atlassian.fugue.law.IsEq} where multiply(n,a) is equal
102    * to sum(take(n, cycle(a))
103    */
104   public IsEq<A> multiplyEqualRepeatedAppend(final int n, final A a) {
105     return IsEq.isEq(monoid.multiply(n, a), monoid.sum(Iterables.take(n, Iterables.cycle(a))));
106   }
107 
108 }