View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.util.data;
23  
24  import static java.lang.StrictMath.min;
25  import static java.util.Arrays.asList;
26  import static org.opencastproject.util.data.Collections.appendTo;
27  import static org.opencastproject.util.data.Collections.appendToA;
28  import static org.opencastproject.util.data.Collections.appendToM;
29  import static org.opencastproject.util.data.Collections.forc;
30  import static org.opencastproject.util.data.Collections.list;
31  import static org.opencastproject.util.data.Collections.toList;
32  import static org.opencastproject.util.data.Option.none;
33  import static org.opencastproject.util.data.Option.some;
34  import static org.opencastproject.util.data.Tuple.tuple;
35  
36  import org.apache.commons.lang3.ArrayUtils;
37  
38  import java.util.ArrayList;
39  import java.util.Collection;
40  import java.util.Comparator;
41  import java.util.Iterator;
42  import java.util.List;
43  import java.util.NoSuchElementException;
44  
45  public final class Monadics {
46  
47    private Monadics() {
48    }
49  
50    // we need to define a separate interface for each container type
51    // since Java lacks higher-order polymorphism (higher-kinded type) so we cannot
52    // abstract over the container type like this
53    //
54    // interface Functor<F<_>> {
55    // <A, B> F<B> fmap(F<A> a, Function<A, B> f);
56    // }
57    //
58    // or
59    //
60    // interface Functor<A, F<A>> {
61    // <B> F<B> fmap(Function<A, B> f);
62    // }
63  
64    /** The list monad. */
65    public abstract static class ListMonadic<A> implements Iterable<A> {
66  
67      private ListMonadic() {
68      }
69  
70      /** Alias for {@link #fmap(Function) fmap}. */
71      public final <B> ListMonadic<B> map(Function<? super A, ? extends B> f) {
72        return fmap(f);
73      }
74  
75      /**
76       * Apply <code>f</code> to each elements building a new list. This is the list functor.
77       *
78       * @see #map(Function)
79       */
80      public abstract <B> ListMonadic<B> fmap(Function<? super A, ? extends B> f);
81  
82      /** Alias for {@link #bind(Function)}. */
83      public final <B> ListMonadic<B> flatMap(Function<? super A, ? extends Iterable<B>> f) {
84        return bind(f);
85      }
86  
87      /**
88       * Monadic bind <code>m a -&gt; (a -&gt; m b) -&gt; m b</code>. Apply <code>f</code> to each elements concatenating
89       * the results into a new list.
90       */
91      public abstract <B> ListMonadic<B> bind(Function<? super A, ? extends Iterable<B>> f);
92  
93      /** Fold the list from left to right applying binary operator <code>f</code> starting with <code>zero</code>. */
94      public abstract <B> B foldl(B zero, Function2<? super B, ? super A, ? extends B> f);
95  
96      /** Reduce the list from left to right applying binary operator <code>f</code>. The list must not be empty. */
97      public abstract A reducel(Function2<? super A, ? super A, ? extends A> f);
98  
99      /** Append <code>a</code> to the list. */
100     public abstract <M extends Iterable<A>> ListMonadic<A> concat(M m);
101 
102     /** Construct a new list by prepending <code>a</code>. */
103     public abstract <X extends A> ListMonadic<A> cons(X a);
104 
105     /** Retain all elements satisfying predicate <code>p</code>. */
106     public abstract ListMonadic<A> filter(Function<? super A, Boolean> p);
107 
108     /** Return the first element satisfying predicate <code>p</code>. */
109     public abstract Option<A> find(Function<? super A, Boolean> p);
110 
111     /** Check if at least one element satisfies predicate <code>p</code>. */
112     public abstract boolean exists(Function<? super A, Boolean> p);
113 
114     /** Apply side effect <code>e</code> to each element. */
115     public abstract ListMonadic<A> each(Function<? super A, Void> e);
116 
117     public abstract <B, M extends Iterable<B>> ListMonadic<Tuple<A, B>> zip(M bs);
118 
119     public abstract ListMonadic<A> sort(Comparator<A> c);
120 
121     /** Return the head of the list. */
122     public abstract Option<A> headOpt();
123 
124     /** Limit the list to the first <code>n</code> elements. */
125     public abstract ListMonadic<A> take(int n);
126 
127     /** Drop the first <code>n</code> elements of the list. */
128     public abstract ListMonadic<A> drop(int n);
129 
130     public abstract String mkString(String sep);
131 
132     /** Return the wrapped, unmodifiable list. */
133     public abstract List<A> value();
134   }
135 
136   /** The iterator monad. */
137   public abstract static class IteratorMonadic<A> implements Iterable<A> {
138 
139     private IteratorMonadic() {
140     }
141 
142     /** Alias for {@link #fmap(Function)}. */
143     public final <B> IteratorMonadic<B> map(Function<A, B> f) {
144       return fmap(f);
145     }
146 
147     /** Apply <code>f</code> to each element. */
148     public abstract <B> IteratorMonadic<B> fmap(Function<A, B> f);
149 
150     /** Apply <code>f</code> to each element. The function also receives the element's index. */
151     public abstract <B> IteratorMonadic<B> mapIndex(Function2<A, Integer, B> f);
152 
153     /** Alias for {@link #bind(Function)}. */
154     public final <B> IteratorMonadic<B> flatMap(Function<A, Iterator<B>> f) {
155       return bind(f);
156     }
157 
158     /** Monadic bind. Apply <code>f</code> to each elements concatenating the results. */
159     public abstract <B> IteratorMonadic<B> bind(Function<A, Iterator<B>> f);
160 
161     // /**
162     // * Apply <code>f</code> to each elements concatenating the results into a new list.
163     // */
164     // <B, BB extends Collection<B>> IteratorMonadic<B> flatMap(Function<A, BB> f);
165 
166     // /**
167     // * Append <code>a</code> to the list.
168     // */
169     // <M extends Collection<A>> ListMonadic<A> concat(M a);
170 
171     /** Retain all elements satisfying predicate <code>p</code>. */
172     public abstract IteratorMonadic<A> filter(Function<A, Boolean> p);
173 
174     /** Check if at least one element satisfies predicate <code>p</code>. */
175     public abstract boolean exists(Function<A, Boolean> p);
176 
177     /** Limit iteration to the first <code>n</code> elements. */
178     public abstract IteratorMonadic<A> take(int n);
179 
180     /** Apply side effect <code>e</code> to each element. */
181     public abstract IteratorMonadic<A> each(Function<A, Void> e);
182 
183     /** Apply side effect <code>e</code> to each element. Indexed version of {@link #each(Function)}. */
184     public abstract IteratorMonadic<A> eachIndex(Function2<A, Integer, Void> e);
185 
186     /** Return the wrapped iterator. */
187     public abstract Iterator<A> value();
188 
189     /** Evaluate to a list. */
190     public abstract List<A> eval();
191   }
192 
193   private static <A> List<A> newListBuilder() {
194     return new ArrayList<A>();
195   }
196 
197   private static <A> List<A> newListBuilder(int size) {
198     return new ArrayList<A>(size);
199   }
200 
201   // -- matchers and constructors
202 
203   // -- constructors
204 
205   /** Constructor for collections. */
206   public static <A> ListMonadic<A> mlist(final Collection<A> as) {
207     return mlist(new ArrayList<A>(as));
208   }
209 
210   /** Constructor function optimized for lists. */
211   public static <A> ListMonadic<A> mlist(final List<A> as) {
212     return new ListMonadic<A>() {
213       @Override
214       public <B> ListMonadic<B> fmap(Function<? super A, ? extends B> f) {
215         final List<B> target = newListBuilder(as.size());
216         for (A a : as) target.add(f.apply(a));
217         return mlist(target);
218       }
219 
220       @Override
221       public <B> ListMonadic<B> bind(Function<? super A, ? extends Iterable<B>> f) {
222         final List<B> target = newListBuilder();
223         for (A a : as)
224           appendTo(target, f.apply(a));
225         return mlist(target);
226       }
227 
228       @Override
229       public ListMonadic<A> filter(Function<? super A, Boolean> p) {
230         final List<A> target = newListBuilder(as.size());
231         for (A a : as) {
232           if (p.apply(a)) {
233             target.add(a);
234           }
235         }
236         return mlist(target);
237       }
238 
239       @Override
240       public Option<A> find(Function<? super A, Boolean> p) {
241         for (A a : as) {
242           if (p.apply(a))
243             return some(a);
244         }
245         return none();
246       }
247 
248       @Override
249       public boolean exists(Function<? super A, Boolean> p) {
250         for (A a : as) {
251           if (p.apply(a))
252             return true;
253         }
254         return false;
255       }
256 
257       @Override
258       public <B> B foldl(B zero, Function2<? super B, ? super A, ? extends B> f) {
259         B fold = zero;
260         for (A a : as) {
261           fold = f.apply(fold, a);
262         }
263         return fold;
264       }
265 
266       @Override
267       public A reducel(Function2<? super A, ? super A, ? extends A> f) {
268         if (as.size() == 0) {
269           throw new RuntimeException("Cannot reduce an empty list");
270         } else {
271           A fold = as.get(0);
272           for (int i = 1; i < as.size(); i++) {
273             fold = f.apply(fold, as.get(i));
274           }
275           return fold;
276         }
277       }
278 
279       @Override
280       public Option<A> headOpt() {
281         return !as.isEmpty() ? some(head()) : Option.<A> none();
282       }
283 
284       private A head() {
285         return as.get(0);
286       }
287 
288       @Override
289       public ListMonadic<A> take(int n) {
290         return mlist(as.subList(0, min(as.size(), n)));
291       }
292 
293       @Override
294       public ListMonadic<A> drop(int n) {
295         return mlist(as.subList(min(as.size(), n), as.size()));
296       }
297 
298       @Override
299       public <M extends Iterable<A>> ListMonadic<A> concat(M bs) {
300         return mlist(appendToM(Monadics.<A> newListBuilder(), as, bs));
301       }
302 
303       @Override
304       public <X extends A> ListMonadic<A> cons(X a) {
305         return mlist(Collections.<A> cons(a, as));
306       }
307 
308       @Override
309       public ListMonadic<A> each(Function<? super A, Void> e) {
310         for (A a : as)
311           e.apply(a);
312         return this;
313       }
314 
315       @Override
316       public <B, M extends Iterable<B>> ListMonadic<Tuple<A, B>> zip(M m) {
317         final List<Tuple<A, B>> target = newListBuilder();
318         final Iterator<A> asi = as.iterator();
319         final Iterator<B> mi = m.iterator();
320         while (asi.hasNext() && mi.hasNext()) {
321           target.add(tuple(asi.next(), mi.next()));
322         }
323         return mlist(target);
324       }
325 
326       @Override
327       public ListMonadic<A> sort(Comparator<A> c) {
328         final List<A> target = newListBuilder(as.size());
329         target.addAll(as);
330         java.util.Collections.sort(target, c);
331         return mlist(target);
332       }
333 
334       @Override
335       public String mkString(String sep) {
336         return Collections.mkString(as, sep);
337       }
338 
339       @Override
340       public Iterator<A> iterator() {
341         return as.iterator();
342       }
343 
344       @Override
345       public List<A> value() {
346         return java.util.Collections.unmodifiableList(as);
347       }
348     };
349   }
350 
351   /** Constructor function optimized for arrays. */
352   public static <A> ListMonadic<A> mlist(final A... as) {
353     return new ListMonadic<A>() {
354       @Override
355       public <B> ListMonadic<B> fmap(Function<? super A, ? extends B> f) {
356         final List<B> target = newListBuilder(as.length);
357         for (A a : as)
358           target.add(f.apply(a));
359         return mlist(target);
360       }
361 
362       @Override
363       public <B> ListMonadic<B> bind(Function<? super A, ? extends Iterable<B>> f) {
364         final List<B> target = newListBuilder();
365         for (A a : as)
366           appendTo(target, f.apply(a));
367         return mlist(target);
368       }
369 
370       @Override
371       public ListMonadic<A> filter(Function<? super A, Boolean> p) {
372         List<A> target = newListBuilder(as.length);
373         for (A a : as) {
374           if (p.apply(a)) {
375             target.add(a);
376           }
377         }
378         return mlist(target);
379       }
380 
381       @Override
382       public Option<A> find(Function<? super A, Boolean> p) {
383         for (A a : as) {
384           if (p.apply(a))
385             return some(a);
386         }
387         return none();
388       }
389 
390       @Override
391       public boolean exists(Function<? super A, Boolean> p) {
392         for (A a : as) {
393           if (p.apply(a))
394             return true;
395         }
396         return false;
397       }
398 
399       @Override
400       public <B> B foldl(B zero, Function2<? super B, ? super A, ? extends B> f) {
401         B fold = zero;
402         for (A a : as) {
403           fold = f.apply(fold, a);
404         }
405         return fold;
406       }
407 
408       @Override
409       public A reducel(Function2<? super A, ? super A, ? extends A> f) {
410         if (as.length == 0) {
411           throw new RuntimeException("Cannot reduce an empty list");
412         } else {
413           A fold = as[0];
414           for (int i = 1; i < as.length; i++) {
415             fold = f.apply(fold, as[i]);
416           }
417           return fold;
418         }
419       }
420 
421       @Override
422       public Option<A> headOpt() {
423         return as.length != 0 ? some(as[0]) : Option.<A> none();
424       }
425 
426       @Override
427       public ListMonadic<A> take(int n) {
428         return (ListMonadic<A>) mlist(ArrayUtils.subarray(as, 0, n));
429       }
430 
431       @Override
432       public ListMonadic<A> drop(int n) {
433         return (ListMonadic<A>) mlist(ArrayUtils.subarray(as, n, as.length));
434       }
435 
436       @Override
437       public <M extends Iterable<A>> ListMonadic<A> concat(M bs) {
438         final List<A> t = newListBuilder(as.length);
439         return mlist(appendTo(appendToA(t, as), bs));
440       }
441 
442       @Override
443       public <X extends A> ListMonadic<A> cons(X a) {
444         return mlist(Collections.<A, List> concat(Collections.<A> list(a), Collections.<A> list(as)));
445       }
446 
447       @Override
448       public ListMonadic<A> each(Function<? super A, Void> e) {
449         for (A a : as) {
450           e.apply(a);
451         }
452         return mlist(as);
453       }
454 
455       @Override
456       public <B, M extends Iterable<B>> ListMonadic<Tuple<A, B>> zip(M m) {
457         final List<Tuple<A, B>> target = newListBuilder();
458         int i = 0;
459         final Iterator<B> mi = m.iterator();
460         while (i < as.length && mi.hasNext()) {
461           target.add(tuple(as[i++], mi.next()));
462         }
463         return mlist(target);
464       }
465 
466       @Override
467       public ListMonadic<A> sort(Comparator<A> c) {
468         final List<A> target = list(as);
469         java.util.Collections.sort(target, c);
470         return mlist(target);
471       }
472 
473       @Override
474       public String mkString(String sep) {
475         return Arrays.mkString(as, sep);
476       }
477 
478       @Override
479       public Iterator<A> iterator() {
480         return Collections.iterator(as);
481       }
482 
483       @Override
484       public List<A> value() {
485         return asList(as);
486       }
487     };
488   }
489 
490   /** Constructor function optimized for iterators. */
491   public static <A> ListMonadic<A> mlist(final Iterator<A> as) {
492     return new ListMonadic<A>() {
493       @Override
494       public <B> ListMonadic<B> fmap(Function<? super A, ? extends B> f) {
495         final List<B> target = newListBuilder();
496         while (as.hasNext()) {
497           target.add(f.apply(as.next()));
498         }
499         return mlist(target);
500       }
501 
502       @Override
503       public <B> ListMonadic<B> bind(Function<? super A, ? extends Iterable<B>> f) {
504         final List<B> target = newListBuilder();
505         while (as.hasNext())
506           appendTo(target, f.apply(as.next()));
507         return mlist(target);
508       }
509 
510       @Override
511       public ListMonadic<A> filter(Function<? super A, Boolean> p) {
512         final List<A> target = newListBuilder();
513         while (as.hasNext()) {
514           A a = as.next();
515           if (p.apply(a)) {
516             target.add(a);
517           }
518         }
519         return mlist(target);
520       }
521 
522       @Override
523       public Option<A> find(Function<? super A, Boolean> p) {
524         for (A a : forc(as)) {
525           if (p.apply(a))
526             return some(a);
527         }
528         return none();
529       }
530 
531       @Override
532       public boolean exists(Function<? super A, Boolean> p) {
533         for (A a : forc(as)) {
534           if (p.apply(a))
535             return true;
536         }
537         return false;
538       }
539 
540       @Override
541       public <B> B foldl(B zero, Function2<? super B, ? super A, ? extends B> f) {
542         B fold = zero;
543         while (as.hasNext()) {
544           fold = f.apply(fold, as.next());
545         }
546         return fold;
547       }
548 
549       @Override
550       public A reducel(Function2<? super A, ? super A, ? extends A> f) {
551         if (!as.hasNext()) {
552           throw new RuntimeException("Cannot reduce an empty iterator");
553         } else {
554           A fold = as.next();
555           while (as.hasNext()) {
556             fold = f.apply(fold, as.next());
557           }
558           return fold;
559         }
560       }
561 
562       @Override
563       public Option<A> headOpt() {
564         throw new UnsupportedOperationException();
565       }
566 
567       @Override
568       public ListMonadic<A> take(final int n) {
569         return mlist(new Iter<A>() {
570           private int count = 0;
571 
572           @Override
573           public boolean hasNext() {
574             return count < n && as.hasNext();
575           }
576 
577           @Override
578           public A next() {
579             if (count < n) {
580               count++;
581               return as.next();
582             } else {
583               throw new NoSuchElementException();
584             }
585           }
586         });
587       }
588 
589       @Override
590       public ListMonadic<A> drop(int n) {
591         int count = n;
592         while (as.hasNext() && count > 0) {
593           as.next();
594           count--;
595         }
596         return mlist(as);
597       }
598 
599       @Override
600       public <M extends Iterable<A>> ListMonadic<A> concat(M bs) {
601         throw new UnsupportedOperationException();
602       }
603 
604       @Override
605       public <X extends A> ListMonadic<A> cons(X a) {
606         return null; // todo
607       }
608 
609       @Override
610       public ListMonadic<A> each(Function<? super A, Void> e) {
611         while (as.hasNext()) e.apply(as.next());
612         return this;
613       }
614 
615       @Override
616       public <B, M extends Iterable<B>> ListMonadic<Tuple<A, B>> zip(M m) {
617         final List<Tuple<A, B>> target = newListBuilder();
618         final Iterator<B> mi = m.iterator();
619         while (as.hasNext() && mi.hasNext()) {
620           target.add(tuple(as.next(), mi.next()));
621         }
622         return mlist(target);
623       }
624 
625       @Override
626       public Iterator<A> iterator() {
627         return as;
628       }
629 
630       @Override
631       public ListMonadic<A> sort(Comparator<A> c) {
632         throw new UnsupportedOperationException();
633       }
634 
635       @Override
636       public String mkString(String sep) {
637         return Collections.mkString(toList(as), sep);
638       }
639 
640       @Override
641       public List<A> value() {
642         return java.util.Collections.unmodifiableList(toList(as));
643       }
644     };
645   }
646 
647   /** Constructor function optimized for iterators. */
648   public static <A> IteratorMonadic<A> mlazy(final Iterator<A> as) {
649     return new IteratorMonadic<A>() {
650       @Override
651       public <B> IteratorMonadic<B> fmap(final Function<A, B> f) {
652         return mlazy(new Iter<B>() {
653           @Override
654           public boolean hasNext() {
655             return as.hasNext();
656           }
657 
658           @Override
659           public B next() {
660             return f.apply(as.next());
661           }
662         });
663       }
664 
665       @Override
666       public <B> IteratorMonadic<B> mapIndex(final Function2<A, Integer, B> f) {
667         return mlazy(new Iter<B>() {
668           private int i = 0;
669 
670           @Override
671           public boolean hasNext() {
672             return as.hasNext();
673           }
674 
675           @Override
676           public B next() {
677             return f.apply(as.next(), i++);
678           }
679         });
680       }
681 
682       @Override
683       public <B> IteratorMonadic<B> bind(final Function<A, Iterator<B>> f) {
684         return mlazy(new Iter<B>() {
685           @Override
686           public boolean hasNext() {
687             return step.hasNext() || step().hasNext();
688           }
689 
690           @Override
691           public B next() {
692             if (step.hasNext()) {
693               return step.next();
694             } else {
695               return step().next();
696             }
697           }
698 
699           // iterator state management
700           private Iterator<B> step = Monadics.emptyIter();
701 
702           private Iterator<B> step() {
703             while (!step.hasNext() && as.hasNext()) {
704               step = f.apply(as.next());
705             }
706             return step;
707           }
708         });
709       }
710 
711       @Override
712       public boolean exists(Function<A, Boolean> p) {
713         for (A a : forc(as)) {
714           if (p.apply(a))
715             return true;
716         }
717         return false;
718       }
719 
720       @Override
721       public IteratorMonadic<A> filter(final Function<A, Boolean> p) {
722         return mlazy(new Iter<A>() {
723           private A next = null;
724 
725           @Override
726           public boolean hasNext() {
727             if (next != null) {
728               return true;
729             } else {
730               for (A a : forc(as)) {
731                 if (p.apply(a)) {
732                   next = a;
733                   return true;
734                 }
735               }
736               return false;
737             }
738           }
739 
740           @Override
741           public A next() {
742             try {
743               if (next != null || hasNext()) {
744                 return next;
745               } else {
746                 throw new NoSuchElementException();
747               }
748             } finally {
749               next = null;
750             }
751           }
752         });
753       }
754 
755       @Override
756       public IteratorMonadic<A> take(final int n) {
757         return mlazy(new Iter<A>() {
758           private int count = 0;
759 
760           @Override
761           public boolean hasNext() {
762             return count < n && as.hasNext();
763           }
764 
765           @Override
766           public A next() {
767             if (count < n) {
768               count++;
769               return as.next();
770             } else {
771               throw new NoSuchElementException();
772             }
773           }
774         });
775       }
776 
777       @Override
778       public IteratorMonadic<A> each(final Function<A, Void> e) {
779         return mlazy(new Iter<A>() {
780           @Override
781           public boolean hasNext() {
782             return as.hasNext();
783           }
784 
785           @Override
786           public A next() {
787             final A a = as.next();
788             e.apply(a);
789             return a;
790           }
791         });
792       }
793 
794       @Override
795       public IteratorMonadic<A> eachIndex(final Function2<A, Integer, Void> e) {
796         return mlazy(new Iter<A>() {
797           private int i = 0;
798 
799           @Override
800           public boolean hasNext() {
801             return as.hasNext();
802           }
803 
804           @Override
805           public A next() {
806             final A a = as.next();
807             e.apply(a, i++);
808             return a;
809           }
810         });
811       }
812 
813       @Override
814       public Iterator<A> iterator() {
815         return as;
816       }
817 
818       @Override
819       public Iterator<A> value() {
820         return as;
821       }
822 
823       @Override
824       public List<A> eval() {
825         return toList(as);
826       }
827     };
828   }
829 
830   /** Constructor function optimized for lists. */
831   public static <A> IteratorMonadic<A> mlazy(final List<A> as) {
832     return mlazy(as.iterator());
833   }
834 
835   private abstract static class Iter<A> implements Iterator<A> {
836     @Override
837     public final void remove() {
838       throw new UnsupportedOperationException();
839     }
840   }
841 
842   private static <A> Iterator<A> emptyIter() {
843     return new Iter<A>() {
844       @Override
845       public boolean hasNext() {
846         return false;
847       }
848 
849       @Override
850       public A next() {
851         throw new NoSuchElementException();
852       }
853     };
854   }
855 }