View Javadoc

1   package io.atlassian.fugue.optic;
2   
3   import io.atlassian.fugue.*;
4   
5   import java.util.Collections;
6   import java.util.function.BinaryOperator;
7   import java.util.function.Function;
8   import java.util.function.Supplier;
9   import java.util.stream.Stream;
10  
11  /**
12   * A {@link POptional} can be seen as a pair of functions: - `getOrModify: S
13   * => T \/ A` - `set : (B, S) => T`
14   * <p>
15   * A {@link POptional} could also be defined as a weaker {@link PLens} and
16   * weaker {@link PPrism}
17   * <p>
18   * {@link POptional} stands for Polymorphic Optional as it set and modify
19   * methods change a type `A` to `B` and `S` to `T`. {@link Optional} is a
20   * {@link POptional} restricted to monomorphic updates: {{{ type Optional[S, A]
21   * = POptional[S, S, A, A] }}}
22   *
23   * @param <S> the source of a {@link POptional}
24   * @param <T> the modified source of a {@link POptional}
25   * @param <A> the target of a {@link POptional}
26   * @param <B> the modified target of a {@link POptional}
27   */
28  public abstract class POptional<S, T, A, B> {
29  
30    POptional() {
31      super();
32    }
33  
34    /**
35     * get the target of a {@link POptional} or modify the source in case there is
36     * no target
37     */
38    public abstract Either<T, A> getOrModify(S s);
39  
40    /**
41     * get the modified source of a {@link POptional}
42     */
43    public abstract Function<S, T> set(final B b);
44  
45    /**
46     * get the target of a {@link POptional} or nothing if there is no target
47     */
48    public abstract Option<A> getOption(final S s);
49  
50    /**
51     * modify polymorphically the target of a {@link POptional} with an
52     * Applicative function
53     */
54    public abstract <C> Function<S, Function<C, T>> modifyFunctionF(final Function<A, Function<C, B>> f);
55  
56    /**
57     * modify polymorphically the target of a {@link POptional} with an
58     * Applicative function
59     */
60    public abstract <L> Function<S, Either<L, T>> modifyEitherF(final Function<A, Either<L, B>> f);
61  
62    /**
63     * modify polymorphically the target of a {@link POptional} with an
64     * Applicative function
65     */
66    public abstract Function<S, Option<T>> modifyOptionF(Function<A, Option<B>> f);
67  
68    /**
69     * modify polymorphically the target of a {@link POptional} with an
70     * Applicative function
71     */
72    public abstract Function<S, Pair<T, T>> modifyPairF(final Function<A, Pair<B, B>> f);
73  
74    /**
75     * modify polymorphically the target of a {@link POptional} with an
76     * Applicative function
77     */
78    public abstract Function<S, Supplier<T>> modifySupplierF(Function<A, Supplier<B>> f);
79  
80    /**
81     * modify polymorphically the target of a {@link POptional} with an
82     * Applicative function
83     */
84    public abstract Function<S, Iterable<T>> modifyIterableF(final Function<A, Iterable<B>> f);
85  
86    /**
87     * modify polymorphically the target of a {@link POptional} with a function
88     */
89    public abstract Function<S, T> modify(final Function<A, B> f);
90  
91    /**
92     * modify polymorphically the target of a {@link POptional} with a function.
93     * return empty if the {@link POptional} is not matching
94     */
95    public final Function<S, Option<T>> modifyOption(final Function<A, B> f) {
96      return s -> getOption(s).map(__ -> modify(f).apply(s));
97    }
98  
99    /**
100    * set polymorphically the target of a {@link POptional} with a value. return
101    * empty if the {@link POptional} is not matching
102    */
103   public final Function<S, Option<T>> setOption(final B b) {
104     return modifyOption(__ -> (b));
105   }
106 
107   /**
108    * check if a {@link POptional} has a target
109    */
110   public final boolean isMatching(final S s) {
111     return getOption(s).isDefined();
112 
113   }
114 
115   /**
116    * join two {@link POptional} with the same target
117    */
118   public final <S1, T1> POptional<Either<S, S1>, Either<T, T1>, A, B> sum(final POptional<S1, T1, A, B> other) {
119     return pOptional(e -> e.fold(s -> getOrModify(s).left().map(Eithers.toLeft()), s1 -> other.getOrModify(s1).left().map(Eithers.toRight())),
120       b -> e -> e.bimap(set(b), other.set(b)));
121   }
122 
123   public <C> POptional<Pair<S, C>, Pair<T, C>, Pair<A, C>, Pair<B, C>> first() {
124     return pOptional(sc -> getOrModify(sc.left()).bimap(t -> Pair.pair(t, sc.right()), a -> Pair.pair(a, sc.right())),
125       bc -> s_ -> Pair.pair(set(bc.left()).apply(s_.left()), bc.right()));
126   }
127 
128   public <C> POptional<Pair<C, S>, Pair<C, T>, Pair<C, A>, Pair<C, B>> second() {
129     return pOptional(cs -> getOrModify(cs.right()).bimap(t -> Pair.pair(cs.left(), t), a -> Pair.pair(cs.left(), a)),
130       cb -> _s -> Pair.pair(cb.left(), set(cb.right()).apply(_s.right())));
131   }
132 
133   /***************************************************************/
134   /** Compose methods between a {@link POptional} and another Optics */
135   /***************************************************************/
136 
137   /**
138    * compose a {@link POptional} with a {@link Fold}
139    */
140   public final <C> Fold<S, C> composeFold(final Fold<A, C> other) {
141     return asFold().composeFold(other);
142   }
143 
144   /**
145    * compose a {@link POptional} with a {@link Getter}
146    */
147   public final <C> Fold<S, C> composeGetter(final Getter<A, C> other) {
148     return asFold().composeGetter(other);
149   }
150 
151   /**
152    * compose a {@link POptional} with a {@link PSetter}
153    */
154   public final <C, D> PSetter<S, T, C, D> composeSetter(final PSetter<A, B, C, D> other) {
155     return asSetter().composeSetter(other);
156   }
157 
158   /**
159    * compose a {@link POptional} with a {@link PTraversal}
160    */
161   public final <C, D> PTraversal<S, T, C, D> composeTraversal(final PTraversal<A, B, C, D> other) {
162     return asTraversal().composeTraversal(other);
163   }
164 
165   /**
166    * compose a {@link POptional} with a {@link POptional}
167    */
168   public final <C, D> POptional<S, T, C, D> composeOptional(final POptional<A, B, C, D> other) {
169     final POptional<S, T, A, B> self = this;
170     return new POptional<S, T, C, D>() {
171 
172       @Override public Either<T, C> getOrModify(final S s) {
173         return self.getOrModify(s).right().flatMap(a -> other.getOrModify(a).bimap(b -> POptional.this.set(b).apply(s), Function.identity()));
174       }
175 
176       @Override public Function<S, T> set(final D d) {
177         return self.modify(other.set(d));
178       }
179 
180       @Override public Option<C> getOption(final S s) {
181         return self.getOption(s).flatMap(other::getOption);
182       }
183 
184       @Override public <G> Function<S, Function<G, T>> modifyFunctionF(final Function<C, Function<G, D>> f) {
185         return self.modifyFunctionF(other.modifyFunctionF(f));
186       }
187 
188       @Override public <L> Function<S, Either<L, T>> modifyEitherF(final Function<C, Either<L, D>> f) {
189         return self.modifyEitherF(other.modifyEitherF(f));
190       }
191 
192       @Override public Function<S, Option<T>> modifyOptionF(final Function<C, Option<D>> f) {
193         return self.modifyOptionF(other.modifyOptionF(f));
194       }
195 
196       @Override public Function<S, Iterable<T>> modifyIterableF(final Function<C, Iterable<D>> f) {
197         return self.modifyIterableF(other.modifyIterableF(f));
198       }
199 
200       @Override public Function<S, Supplier<T>> modifySupplierF(final Function<C, Supplier<D>> f) {
201         return self.modifySupplierF(other.modifySupplierF(f));
202       }
203 
204       @Override public Function<S, Pair<T, T>> modifyPairF(final Function<C, Pair<D, D>> f) {
205         return self.modifyPairF(other.modifyPairF(f));
206       }
207 
208       @Override public Function<S, T> modify(final Function<C, D> f) {
209         return self.modify(other.modify(f));
210       }
211     };
212   }
213 
214   /**
215    * compose a {@link POptional} with a {@link PPrism}
216    */
217   public final <C, D> POptional<S, T, C, D> composePrism(final PPrism<A, B, C, D> other) {
218     return composeOptional(other.asOptional());
219   }
220 
221   /**
222    * compose a {@link POptional} with a {@link PLens}
223    */
224   public final <C, D> POptional<S, T, C, D> composeLens(final PLens<A, B, C, D> other) {
225     return composeOptional(other.asOptional());
226   }
227 
228   /**
229    * compose a {@link POptional} with a {@link PIso}
230    */
231   public final <C, D> POptional<S, T, C, D> composeIso(final PIso<A, B, C, D> other) {
232     return composeOptional(other.asOptional());
233   }
234 
235   /*********************************************************************/
236   /** Transformation methods to view a {@link POptional} as another Optics */
237   /*********************************************************************/
238 
239   /**
240    * view a {@link POptional} as a {@link Fold}
241    */
242   public final Fold<S, A> asFold() {
243     return new Fold<S, A>() {
244       @Override public <M> Function<S, M> foldMap(final Monoid<M> monoid, final Function<A, M> f) {
245         return s -> POptional.this.getOption(s).map(f).getOrElse(monoid.zero());
246       }
247     };
248   }
249 
250   /**
251    * view a {@link POptional} as a {@link PSetter}
252    */
253   public PSetter<S, T, A, B> asSetter() {
254     return new PSetter<S, T, A, B>() {
255       @Override public Function<S, T> modify(final Function<A, B> f) {
256         return POptional.this.modify(f);
257       }
258 
259       @Override public Function<S, T> set(final B b) {
260         return POptional.this.set(b);
261       }
262     };
263   }
264 
265   /**
266    * view a {@link POptional} as a {@link PTraversal}
267    */
268   public PTraversal<S, T, A, B> asTraversal() {
269     final POptional<S, T, A, B> self = this;
270     return new PTraversal<S, T, A, B>() {
271 
272       @Override public <C> Function<S, Function<C, T>> modifyFunctionF(final Function<A, Function<C, B>> f) {
273         return self.modifyFunctionF(f);
274       }
275 
276       @Override public <L> Function<S, Either<L, T>> modifyEitherF(final Function<A, Either<L, B>> f) {
277         return self.modifyEitherF(f);
278       }
279 
280       @Override public Function<S, Option<T>> modifyOptionF(final Function<A, Option<B>> f) {
281         return self.modifyOptionF(f);
282       }
283 
284       @Override public Function<S, Iterable<T>> modifyIterableF(final Function<A, Iterable<B>> f) {
285         return self.modifyIterableF(f);
286       }
287 
288       @Override public Function<S, Supplier<T>> modifySupplierF(final Function<A, Supplier<B>> f) {
289         return self.modifySupplierF(f);
290       }
291 
292       @Override public Function<S, Pair<T, T>> modifyPairF(final Function<A, Pair<B, B>> f) {
293         return self.modifyPairF(f);
294       }
295 
296       @Override public <M> Function<S, M> foldMap(final Monoid<M> monoid, final Function<A, M> f) {
297         return s -> self.getOption(s).map(f).getOrElse(monoid.zero());
298       }
299     };
300   }
301 
302   public static <S, T> POptional<S, T, S, T> pId() {
303     return PIso.<S, T> pId().asOptional();
304   }
305 
306   /**
307    * create a {@link POptional} using the canonical functions: getOrModify and
308    * set
309    */
310   public static <S, T, A, B> POptional<S, T, A, B> pOptional(final Function<S, Either<T, A>> getOrModify, final Function<B, Function<S, T>> set) {
311     return new POptional<S, T, A, B>() {
312       @Override public Either<T, A> getOrModify(final S s) {
313         return getOrModify.apply(s);
314       }
315 
316       @Override public Function<S, T> set(final B b) {
317         return set.apply(b);
318       }
319 
320       @Override public Option<A> getOption(final S s) {
321         return getOrModify.apply(s).right().toOption();
322       }
323 
324       @Override public <C> Function<S, Function<C, T>> modifyFunctionF(final Function<A, Function<C, B>> f) {
325         return s -> getOrModify.apply(s).fold(t -> __ -> t, a -> f.apply(a).andThen(b -> set.apply(b).apply(s)));
326       }
327 
328       @Override public <L> Function<S, Either<L, T>> modifyEitherF(final Function<A, Either<L, B>> f) {
329         return s -> getOrModify.apply(s).fold(Eithers.toRight(), t -> f.apply(t).right().map(b -> set.apply(b).apply(s)));
330       }
331 
332       @Override public Function<S, Option<T>> modifyOptionF(final Function<A, Option<B>> f) {
333         return s -> getOrModify.apply(s).fold(Option::some, t -> f.apply(t).map(b -> set.apply(b).apply(s)));
334       }
335 
336       @Override public Function<S, Iterable<T>> modifyIterableF(final Function<A, Iterable<B>> f) {
337         return s -> getOrModify.apply(s).fold(Collections::singleton, t -> Iterables.<B, T> map(f.apply(t), b -> set.apply(b).apply(s)));
338       }
339 
340       @Override public Function<S, Supplier<T>> modifySupplierF(final Function<A, Supplier<B>> f) {
341         return s -> getOrModify.apply(s).fold(Suppliers::ofInstance, t -> Suppliers.<B, T> compose(b -> set.apply(b).apply(s), f.apply(t)));
342       }
343 
344       @Override public Function<S, Pair<T, T>> modifyPairF(final Function<A, Pair<B, B>> f) {
345         return s -> getOrModify.apply(s).fold(t -> Pair.pair(t, t), t -> Pair.<B, T> map(f.apply(t), b -> set.apply(b).apply(s)));
346       }
347 
348       @Override public Function<S, T> modify(final Function<A, B> f) {
349         return s -> getOrModify.apply(s).fold(Function.identity(), a -> set.apply(f.apply(a)).apply(s));
350       }
351     };
352   }
353 }