View Javadoc
1   package io.atlassian.fugue.optic;
2   
3   import io.atlassian.fugue.Either;
4   import io.atlassian.fugue.Monoid;
5   import io.atlassian.fugue.Monoids;
6   import io.atlassian.fugue.Option;
7   
8   import java.util.Collections;
9   import java.util.function.Function;
10  import java.util.function.Predicate;
11  
12  /**
13   * A {@link Fold} can be seen as a {@link Getter} with many targets or a weaker
14   * {@link PTraversal} which cannot modify its target.
15   * <p>
16   * {@link Fold} is on the top of the Optic hierarchy which means that
17   * {@link Getter}, {@link PTraversal}, {@link POptional}, {@link PLens},
18   * {@link PPrism} and {@link PIso} are valid {@link Fold}
19   *
20   * @param <S> the source of a {@link Fold}
21   * @param <A> the target of a {@link Fold}
22   */
23  public abstract class Fold<S, A> {
24  
25    /**
26     * map each target to a {@link Monoid} and combine the results underlying
27     * representation of {@link Fold}, all {@link Fold} methods are defined in
28     * terms of foldMap
29     */
30    public abstract <M> Function<S, M> foldMap(Monoid<M> monoid, Function<A, M> f);
31  
32    /**
33     * combine all targets using a target's {@link Monoid}
34     */
35    public final Function<S, A> fold(Monoid<A> monoid) {
36      return foldMap(monoid, Function.identity());
37    }
38  
39    /**
40     * get all the targets of a {@link Fold}
41     */
42    public final Iterable<A> getAll(final S s) {
43      return foldMap(Monoids.iterable(), Collections::singleton).apply(s);
44    }
45  
46    /**
47     * find the first target of a {@link Fold} matching the predicate
48     */
49    public final Function<S, Option<A>> find(final Predicate<A> p) {
50      return foldMap(Monoids.firstOption(), a -> p.test(a) ? Option.some(a) : Option.none());
51    }
52  
53    /**
54     * get the first target of a {@link Fold}
55     */
56    public final Option<A> headOption(final S s) {
57      return find(__ -> true).apply(s);
58    }
59  
60    /**
61     * check if at least one target satisfies the predicate
62     */
63    public final Predicate<S> exist(final Predicate<A> p) {
64      return foldMap(Monoids.disjunction, p::test)::apply;
65    }
66  
67    /**
68     * check if all targets satisfy the predicate
69     */
70    public final Function<S, Boolean> all(final Predicate<A> p) {
71      return foldMap(Monoids.conjunction, p::test)::apply;
72    }
73  
74    /**
75     * join two {@link Fold} with the same target
76     */
77    public final <S1> Fold<Either<S, S1>, A> sum(final Fold<S1, A> other) {
78      return new Fold<Either<S, S1>, A>() {
79        @Override public <B> Function<Either<S, S1>, B> foldMap(final Monoid<B> monoid, final Function<A, B> f) {
80          return s -> s.fold(Fold.this.foldMap(monoid, f), other.foldMap(monoid, f));
81        }
82      };
83    }
84  
85    /**********************************************************/
86    /** Compose methods between a {@link Fold} and another Optics */
87    /**********************************************************/
88  
89    /**
90     * compose a {@link Fold} with a {@link Fold}
91     */
92    public final <B> Fold<S, B> composeFold(final Fold<A, B> other) {
93      return new Fold<S, B>() {
94        @Override public <C> Function<S, C> foldMap(final Monoid<C> monoid, final Function<B, C> f) {
95          return Fold.this.foldMap(monoid, other.foldMap(monoid, f));
96        }
97      };
98    }
99  
100   /**
101    * compose a {@link Fold} with a {@link Getter}
102    */
103   public final <C> Fold<S, C> composeGetter(final Getter<A, C> other) {
104     return composeFold(other.asFold());
105   }
106 
107   /**
108    * compose a {@link Fold} with a {@link POptional}
109    */
110   public final <B, C, D> Fold<S, C> composeOptional(final POptional<A, B, C, D> other) {
111     return composeFold(other.asFold());
112   }
113 
114   /**
115    * compose a {@link Fold} with a {@link PPrism}
116    */
117   public final <B, C, D> Fold<S, C> composePrism(final PPrism<A, B, C, D> other) {
118     return composeFold(other.asFold());
119   }
120 
121   /**
122    * compose a {@link Fold} with a {@link PLens}
123    */
124   public final <B, C, D> Fold<S, C> composeLens(final PLens<A, B, C, D> other) {
125     return composeFold(other.asFold());
126   }
127 
128   /**
129    * compose a {@link Fold} with a {@link PIso}
130    */
131   public final <B, C, D> Fold<S, C> composeIso(final PIso<A, B, C, D> other) {
132     return composeFold(other.asFold());
133   }
134 
135   public static <A> Fold<A, A> id() {
136     return PIso.<A, A> pId().asFold();
137   }
138 
139   public static <A> Fold<Either<A, A>, A> codiagonal() {
140     return new Fold<Either<A, A>, A>() {
141       @Override public <B> Function<Either<A, A>, B> foldMap(final Monoid<B> monoid, final Function<A, B> f) {
142         return e -> e.fold(f, f);
143       }
144     };
145   }
146 
147 }