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