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