/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at:
*
* http://opensource.org/licenses/ecl2.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.opencastproject.util.data;
import static java.lang.StrictMath.min;
import static java.util.Arrays.asList;
import static org.opencastproject.util.data.Collections.appendTo;
import static org.opencastproject.util.data.Collections.appendToA;
import static org.opencastproject.util.data.Collections.appendToM;
import static org.opencastproject.util.data.Collections.forc;
import static org.opencastproject.util.data.Collections.iterator;
import static org.opencastproject.util.data.Collections.list;
import static org.opencastproject.util.data.Collections.toList;
import static org.opencastproject.util.data.Option.none;
import static org.opencastproject.util.data.Option.some;
import static org.opencastproject.util.data.Prelude.unexhaustiveMatch;
import static org.opencastproject.util.data.Tuple.tuple;
import org.apache.commons.lang3.ArrayUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public final class Monadics {
private Monadics() {
}
// we need to define a separate interface for each container type
// since Java lacks higher-order polymorphism (higher-kinded type) so we cannot
// abstract over the container type like this
//
// interface Functor<F<_>> {
// <A, B> F<B> fmap(F<A> a, Function<A, B> f);
// }
//
// or
//
// interface Functor<A, F<A>> {
// <B> F<B> fmap(Function<A, B> f);
// }
/** The list monad. */
public abstract static class ListMonadic<A> implements Iterable<A> {
private ListMonadic() {
}
/** Alias for {@link #fmap(Function) fmap}. */
public final <B> ListMonadic<B> map(Function<? super A, ? extends B> f) {
return fmap(f);
}
/**
* Apply <code>f</code> to each elements building a new list. This is the list functor.
*
* @see #map(Function)
*/
public abstract <B> ListMonadic<B> fmap(Function<? super A, ? extends B> f);
/** Alias for {@link #bind(Function)}. */
public final <B> ListMonadic<B> flatMap(Function<? super A, ? extends Iterable<B>> f) {
return bind(f);
}
/**
* Monadic bind <code>m a -> (a -> m b) -> m b</code>. Apply <code>f</code> to each elements concatenating
* the results into a new list.
*/
public abstract <B> ListMonadic<B> bind(Function<? super A, ? extends Iterable<B>> f);
/** Fold the list from left to right applying binary operator <code>f</code> starting with <code>zero</code>. */
public abstract <B> B foldl(B zero, Function2<? super B, ? super A, ? extends B> f);
/** Reduce the list from left to right applying binary operator <code>f</code>. The list must not be empty. */
public abstract A reducel(Function2<? super A, ? super A, ? extends A> f);
/** Append <code>a</code> to the list. */
public abstract <M extends Iterable<A>> ListMonadic<A> concat(M m);
/** Construct a new list by prepending <code>a</code>. */
public abstract <X extends A> ListMonadic<A> cons(X a);
/** Retain all elements satisfying predicate <code>p</code>. */
public abstract ListMonadic<A> filter(Function<? super A, Boolean> p);
/** Return the first element satisfying predicate <code>p</code>. */
public abstract Option<A> find(Function<? super A, Boolean> p);
/** Check if at least one element satisfies predicate <code>p</code>. */
public abstract boolean exists(Function<? super A, Boolean> p);
/** Apply side effect <code>e</code> to each element. */
public abstract ListMonadic<A> each(Function<? super A, Void> e);
/** Apply side effect <code>e</code> to each element. Indexed version. */
public abstract ListMonadic<A> eachIndex(Function2<? super A, ? super Integer, Void> e);
public abstract <B, M extends Iterable<B>> ListMonadic<Tuple<A, B>> zip(M bs);
public abstract <B> ListMonadic<Tuple<A, B>> zip(B[] bs);
public abstract <B> ListMonadic<Tuple<A, B>> zip(Iterator<B> bs);
public abstract ListMonadic<A> sort(Comparator<A> c);
public abstract ListMonadic<A> reverse();
/** Return the head of the list. */
public abstract Option<A> headOpt();
/** Return the head of the list. */
public abstract A head();
/** Return the last element of the list. */
public abstract A last();
/** Return the last element of the list. */
public abstract Option<A> lastOpt();
/** Turn the list into an option only if it contains exactly one element. */
public abstract Option<A> option();
/** Return the tail of the list. */
public abstract ListMonadic<A> tail();
/** Limit the list to the first <code>n</code> elements. */
public abstract ListMonadic<A> take(int n);
/** Drop the first <code>n</code> elements of the list. */
public abstract ListMonadic<A> drop(int n);
/** Process the wrapped list en bloc. */
public abstract <B> ListMonadic<B> inspect(Function<? super List<A>, ? extends List<B>> f);
/** Pattern matching on the wrapped list. */
public final <B> B match(Matcher<A, B>... ms) {
return match(list(ms));
}
/** Pattern matching on the wrapped list. */
public final <B> B match(List<Matcher<A, B>> ms) {
for (Matcher<A, B> m : ms) {
if (m.matches(this)) {
return m.apply(this);
}
}
return unexhaustiveMatch();
}
public abstract String mkString(String sep);
/** Return the wrapped, unmodifiable list. */
public abstract List<A> value();
}
/** The iterator monad. */
public abstract static class IteratorMonadic<A> implements Iterable<A> {
private IteratorMonadic() {
}
/** Alias for {@link #fmap(Function)}. */
public final <B> IteratorMonadic<B> map(Function<A, B> f) {
return fmap(f);
}
/** Apply <code>f</code> to each element. */
public abstract <B> IteratorMonadic<B> fmap(Function<A, B> f);
/** Apply <code>f</code> to each element. The function also receives the element's index. */
public abstract <B> IteratorMonadic<B> mapIndex(Function2<A, Integer, B> f);
/** Alias for {@link #bind(Function)}. */
public final <B> IteratorMonadic<B> flatMap(Function<A, Iterator<B>> f) {
return bind(f);
}
/** Monadic bind. Apply <code>f</code> to each elements concatenating the results. */
public abstract <B> IteratorMonadic<B> bind(Function<A, Iterator<B>> f);
// /**
// * Apply <code>f</code> to each elements concatenating the results into a new list.
// */
// <B, BB extends Collection<B>> IteratorMonadic<B> flatMap(Function<A, BB> f);
/** Fold the elements applying binary operator <code>f</code> starting with <code>zero</code>. */
public abstract <B> B fold(B zero, Function2<B, A, B> f);
/** Reduce the elements applying binary operator <code>f</code>. The iterator must not be empty. */
public abstract A reduce(Function2<A, A, A> f);
// /**
// * Append <code>a</code> to the list.
// */
// <M extends Collection<A>> ListMonadic<A> concat(M a);
/** Retain all elements satisfying predicate <code>p</code>. */
public abstract IteratorMonadic<A> filter(Function<A, Boolean> p);
/** Check if at least one element satisfies predicate <code>p</code>. */
public abstract boolean exists(Function<A, Boolean> p);
/** Limit iteration to the first <code>n</code> elements. */
public abstract IteratorMonadic<A> take(int n);
/** Apply side effect <code>e</code> to each element. */
public abstract IteratorMonadic<A> each(Function<A, Void> e);
/** Apply side effect <code>e</code> to each element. Indexed version of {@link #each(Function)}. */
public abstract IteratorMonadic<A> eachIndex(Function2<A, Integer, Void> e);
/**
* Return the head of the iterator. <em>ATTENTION:</em> This method is not pure since it has the side effect of
* taking and wrapping the next element of the wrapped iterator.
*/
public abstract Option<A> next();
/** Return the wrapped iterator. */
public abstract Iterator<A> value();
/** Evaluate to a list. */
public abstract List<A> eval();
}
private static <A> List<A> newListBuilder() {
return new ArrayList<A>();
}
private static <A> List<A> newListBuilder(int size) {
return new ArrayList<A>(size);
}
private static <A> List<A> newListBuilder(Collection<A> as) {
return new ArrayList<A>(as);
}
// -- matchers and constructors
public interface Matcher<A, B> {
boolean matches(ListMonadic<A> m);
B apply(ListMonadic<A> m);
}
/** Matches the empty list. Like Haskell's <code>[]</code> */
public static <A, B> Matcher<A, B> caseNil(final Function0<B> f) {
return new Matcher<A, B>() {
@Override public boolean matches(ListMonadic<A> m) {
return m.value().size() == 0;
}
@Override public B apply(ListMonadic<A> m) {
return f.apply();
}
};
}
/** Matches lists with exactly one element. Like Haskell's <code>(x:[])</code> */
public static <A, B> Matcher<A, B> caseA(final Function<A, B> f) {
return new Matcher<A, B>() {
@Override public boolean matches(ListMonadic<A> m) {
return m.value().size() == 1;
}
@Override public B apply(ListMonadic<A> m) {
return f.apply(m.head());
}
};
}
/** Matches lists with at least one element. Like Haskell's <code>(x:xs)</code> */
public static <A, B> Matcher<A, B> caseAN(final Function2<A, List<A>, B> f) {
return new Matcher<A, B>() {
@Override public boolean matches(ListMonadic<A> m) {
return m.value().size() >= 1;
}
@Override public B apply(ListMonadic<A> m) {
return f.apply(m.head(), m.tail().value());
}
};
}
/** Matches any list. Like Haskell's <code>(xs)</code> */
public static <A, B> Matcher<A, B> caseN(final Function<List<A>, B> f) {
return new Matcher<A, B>() {
@Override public boolean matches(ListMonadic<A> m) {
return true;
}
@Override public B apply(ListMonadic<A> m) {
return f.apply(m.value());
}
};
}
// -- constructors
public static <A> ListMonadic<A> mlist(final Iterable<A> as) {
return mlist(as.iterator());
}
/** Constructor for collections. */
public static <A> ListMonadic<A> mlist(final Collection<A> as) {
return mlist(new ArrayList<A>(as));
}
/** Constructor function optimized for lists. */
public static <A> ListMonadic<A> mlist(final List<A> as) {
return new ListMonadic<A>() {
@Override
public <B> ListMonadic<B> fmap(Function<? super A, ? extends B> f) {
final List<B> target = newListBuilder(as.size());
for (A a : as) target.add(f.apply(a));
return mlist(target);
}
@Override
public <B> ListMonadic<B> bind(Function<? super A, ? extends Iterable<B>> f) {
final List<B> target = newListBuilder();
for (A a : as)
appendTo(target, f.apply(a));
return mlist(target);
}
@Override
public ListMonadic<A> filter(Function<? super A, Boolean> p) {
final List<A> target = newListBuilder(as.size());
for (A a : as) {
if (p.apply(a)) {
target.add(a);
}
}
return mlist(target);
}
@Override
public Option<A> find(Function<? super A, Boolean> p) {
for (A a : as) {
if (p.apply(a))
return some(a);
}
return none();
}
@Override
public boolean exists(Function<? super A, Boolean> p) {
for (A a : as) {
if (p.apply(a))
return true;
}
return false;
}
@Override
public <B> B foldl(B zero, Function2<? super B, ? super A, ? extends B> f) {
B fold = zero;
for (A a : as) {
fold = f.apply(fold, a);
}
return fold;
}
@Override
public A reducel(Function2<? super A, ? super A, ? extends A> f) {
if (as.size() == 0) {
throw new RuntimeException("Cannot reduce an empty list");
} else {
A fold = as.get(0);
for (int i = 1; i < as.size(); i++) {
fold = f.apply(fold, as.get(i));
}
return fold;
}
}
@Override
public Option<A> headOpt() {
return !as.isEmpty() ? some(head()) : Option.<A> none();
}
@Override
public A head() {
return as.get(0);
}
@Override
public A last() {
return as.get(as.size() - 1);
}
@Override
public Option<A> lastOpt() {
return !as.isEmpty() ? some(last()) : Option.<A> none();
}
@Override
public Option<A> option() {
return as.size() == 1 ? some(as.get(0)) : Option.<A> none();
}
@Override
public ListMonadic<A> tail() {
if (as.size() <= 1)
return mlist();
return mlist(as.subList(1, as.size()));
}
@Override
public ListMonadic<A> take(int n) {
return mlist(as.subList(0, min(as.size(), n)));
}
@Override
public ListMonadic<A> drop(int n) {
return mlist(as.subList(min(as.size(), n), as.size()));
}
@Override
public <M extends Iterable<A>> ListMonadic<A> concat(M bs) {
return mlist(appendToM(Monadics.<A> newListBuilder(), as, bs));
}
@Override
public <X extends A> ListMonadic<A> cons(X a) {
return mlist(Collections.<A> cons(a, as));
}
@Override
public <B> ListMonadic<B> inspect(Function<? super List<A>, ? extends List<B>> f) {
return mlist(f.apply(as));
}
@Override
public ListMonadic<A> each(Function<? super A, Void> e) {
for (A a : as)
e.apply(a);
return this;
}
@Override
public ListMonadic<A> eachIndex(Function2<? super A, ? super Integer, Void> e) {
int i = 0;
for (A a : as)
e.apply(a, i++);
return this;
}
@Override
public <B, M extends Iterable<B>> ListMonadic<Tuple<A, B>> zip(M m) {
final List<Tuple<A, B>> target = newListBuilder();
final Iterator<A> asi = as.iterator();
final Iterator<B> mi = m.iterator();
while (asi.hasNext() && mi.hasNext()) {
target.add(tuple(asi.next(), mi.next()));
}
return mlist(target);
}
@Override
public <B> ListMonadic<Tuple<A, B>> zip(B[] bs) {
final List<Tuple<A, B>> target = newListBuilder(min(as.size(), bs.length));
int i = 0;
final Iterator<A> asi = as.iterator();
while (asi.hasNext() && i < bs.length) {
target.add(tuple(asi.next(), bs[i++]));
}
return mlist(target);
}
@Override
public <B> ListMonadic<Tuple<A, B>> zip(Iterator<B> bs) {
final List<Tuple<A, B>> target = newListBuilder(as.size());
final Iterator<A> asi = as.iterator();
while (asi.hasNext() && bs.hasNext()) {
target.add(tuple(asi.next(), bs.next()));
}
return mlist(target);
}
@Override
public ListMonadic<A> sort(Comparator<A> c) {
final List<A> target = newListBuilder(as.size());
target.addAll(as);
java.util.Collections.sort(target, c);
return mlist(target);
}
@Override
public ListMonadic<A> reverse() {
final List<A> target = newListBuilder(as);
java.util.Collections.reverse(target);
return mlist(target);
}
@Override
public String mkString(String sep) {
return Collections.mkString(as, sep);
}
@Override
public Iterator<A> iterator() {
return as.iterator();
}
@Override
public List<A> value() {
return java.util.Collections.unmodifiableList(as);
}
};
}
/** Constructor function optimized for arrays. */
public static <A> ListMonadic<A> mlist(final A... as) {
return new ListMonadic<A>() {
@Override
public <B> ListMonadic<B> fmap(Function<? super A, ? extends B> f) {
final List<B> target = newListBuilder(as.length);
for (A a : as)
target.add(f.apply(a));
return mlist(target);
}
@Override
public <B> ListMonadic<B> bind(Function<? super A, ? extends Iterable<B>> f) {
final List<B> target = newListBuilder();
for (A a : as)
appendTo(target, f.apply(a));
return mlist(target);
}
@Override
public ListMonadic<A> filter(Function<? super A, Boolean> p) {
List<A> target = newListBuilder(as.length);
for (A a : as) {
if (p.apply(a)) {
target.add(a);
}
}
return mlist(target);
}
@Override
public Option<A> find(Function<? super A, Boolean> p) {
for (A a : as) {
if (p.apply(a))
return some(a);
}
return none();
}
@Override
public boolean exists(Function<? super A, Boolean> p) {
for (A a : as) {
if (p.apply(a))
return true;
}
return false;
}
@Override
public <B> B foldl(B zero, Function2<? super B, ? super A, ? extends B> f) {
B fold = zero;
for (A a : as) {
fold = f.apply(fold, a);
}
return fold;
}
@Override
public A reducel(Function2<? super A, ? super A, ? extends A> f) {
if (as.length == 0) {
throw new RuntimeException("Cannot reduce an empty list");
} else {
A fold = as[0];
for (int i = 1; i < as.length; i++) {
fold = f.apply(fold, as[i]);
}
return fold;
}
}
@Override
public Option<A> headOpt() {
return as.length != 0 ? some(as[0]) : Option.<A> none();
}
@Override
public A head() {
return as[0];
}
@Override
public A last() {
return as[as.length - 1];
}
@Override
public Option<A> lastOpt() {
return as.length > 0 ? some(last()) : Option.<A> none();
}
@Override
public Option<A> option() {
return as.length == 1 ? some(as[0]) : Option.<A> none();
}
@Override
public ListMonadic<A> tail() {
if (as.length <= 1)
return mlist();
return (ListMonadic<A>) mlist(ArrayUtils.subarray(as, 1, as.length));
}
@Override
public ListMonadic<A> take(int n) {
return (ListMonadic<A>) mlist(ArrayUtils.subarray(as, 0, n));
}
@Override
public ListMonadic<A> drop(int n) {
return (ListMonadic<A>) mlist(ArrayUtils.subarray(as, n, as.length));
}
@Override
public <M extends Iterable<A>> ListMonadic<A> concat(M bs) {
final List<A> t = newListBuilder(as.length);
return mlist(appendTo(appendToA(t, as), bs));
}
@Override
public <X extends A> ListMonadic<A> cons(X a) {
return mlist(Collections.<A, List> concat(Collections.<A> list(a), Collections.<A> list(as)));
}
@Override
public <B> ListMonadic<B> inspect(Function<? super List<A>, ? extends List<B>> f) {
return mlist(f.apply(value()));
}
@Override
public ListMonadic<A> each(Function<? super A, Void> e) {
for (A a : as) {
e.apply(a);
}
return mlist(as);
}
@Override
public ListMonadic<A> eachIndex(Function2<? super A, ? super Integer, Void> e) {
int i = 0;
for (A a : as) {
e.apply(a, i++);
}
return this;
}
@Override
public <B, M extends Iterable<B>> ListMonadic<Tuple<A, B>> zip(M m) {
final List<Tuple<A, B>> target = newListBuilder();
int i = 0;
final Iterator<B> mi = m.iterator();
while (i < as.length && mi.hasNext()) {
target.add(tuple(as[i++], mi.next()));
}
return mlist(target);
}
@Override
public <B> ListMonadic<Tuple<A, B>> zip(B[] bs) {
final List<Tuple<A, B>> target = newListBuilder(min(as.length, bs.length));
int i = 0;
while (i < as.length && i < bs.length) {
target.add(tuple(as[i], bs[i]));
i++;
}
return mlist(target);
}
@Override
public <B> ListMonadic<Tuple<A, B>> zip(Iterator<B> bs) {
final List<Tuple<A, B>> target = newListBuilder(as.length);
int i = 0;
while (i < as.length && bs.hasNext()) {
target.add(tuple(as[i++], bs.next()));
}
return mlist(target);
}
@Override
public ListMonadic<A> sort(Comparator<A> c) {
final List<A> target = list(as);
java.util.Collections.sort(target, c);
return mlist(target);
}
@Override
public ListMonadic<A> reverse() {
final List<A> target = list(as);
java.util.Collections.reverse(target);
return mlist(target);
}
@Override
public String mkString(String sep) {
return Arrays.mkString(as, sep);
}
@Override
public Iterator<A> iterator() {
return Collections.iterator(as);
}
@Override
public List<A> value() {
return asList(as);
}
};
}
/** Constructor function optimized for iterators. */
public static <A> ListMonadic<A> mlist(final Iterator<A> as) {
return new ListMonadic<A>() {
@Override
public <B> ListMonadic<B> fmap(Function<? super A, ? extends B> f) {
final List<B> target = newListBuilder();
while (as.hasNext()) {
target.add(f.apply(as.next()));
}
return mlist(target);
}
@Override
public <B> ListMonadic<B> bind(Function<? super A, ? extends Iterable<B>> f) {
final List<B> target = newListBuilder();
while (as.hasNext())
appendTo(target, f.apply(as.next()));
return mlist(target);
}
@Override
public ListMonadic<A> filter(Function<? super A, Boolean> p) {
final List<A> target = newListBuilder();
while (as.hasNext()) {
A a = as.next();
if (p.apply(a)) {
target.add(a);
}
}
return mlist(target);
}
@Override
public Option<A> find(Function<? super A, Boolean> p) {
for (A a : forc(as)) {
if (p.apply(a))
return some(a);
}
return none();
}
@Override
public boolean exists(Function<? super A, Boolean> p) {
for (A a : forc(as)) {
if (p.apply(a))
return true;
}
return false;
}
@Override
public <B> B foldl(B zero, Function2<? super B, ? super A, ? extends B> f) {
B fold = zero;
while (as.hasNext()) {
fold = f.apply(fold, as.next());
}
return fold;
}
@Override
public A reducel(Function2<? super A, ? super A, ? extends A> f) {
if (!as.hasNext()) {
throw new RuntimeException("Cannot reduce an empty iterator");
} else {
A fold = as.next();
while (as.hasNext()) {
fold = f.apply(fold, as.next());
}
return fold;
}
}
@Override
public Option<A> headOpt() {
throw new UnsupportedOperationException();
}
@Override
public A head() {
throw new UnsupportedOperationException();
}
@Override
public A last() {
throw new UnsupportedOperationException();
}
@Override
public Option<A> lastOpt() {
throw new UnsupportedOperationException();
}
@Override
public Option<A> option() {
throw new UnsupportedOperationException();
}
@Override
public ListMonadic<A> tail() {
throw new UnsupportedOperationException();
}
@Override
public ListMonadic<A> take(final int n) {
return mlist(new Iter<A>() {
private int count = 0;
@Override
public boolean hasNext() {
return count < n && as.hasNext();
}
@Override
public A next() {
if (count < n) {
count++;
return as.next();
} else {
throw new NoSuchElementException();
}
}
});
}
@Override
public ListMonadic<A> drop(int n) {
int count = n;
while (as.hasNext() && count > 0) {
as.next();
count--;
}
return mlist(as);
}
@Override
public <M extends Iterable<A>> ListMonadic<A> concat(M bs) {
throw new UnsupportedOperationException();
}
@Override
public <X extends A> ListMonadic<A> cons(X a) {
return null; // todo
}
@Override
public <B> ListMonadic<B> inspect(Function<? super List<A>, ? extends List<B>> f) {
throw new UnsupportedOperationException();
}
@Override
public ListMonadic<A> each(Function<? super A, Void> e) {
while (as.hasNext()) e.apply(as.next());
return this;
}
@Override
public ListMonadic<A> eachIndex(Function2<? super A, ? super Integer, Void> e) {
int i = 0;
while (as.hasNext())
e.apply(as.next(), i++);
return this;
}
@Override
public <B, M extends Iterable<B>> ListMonadic<Tuple<A, B>> zip(M m) {
final List<Tuple<A, B>> target = newListBuilder();
final Iterator<B> mi = m.iterator();
while (as.hasNext() && mi.hasNext()) {
target.add(tuple(as.next(), mi.next()));
}
return mlist(target);
}
@Override
public <B> ListMonadic<Tuple<A, B>> zip(B[] bs) {
final List<Tuple<A, B>> target = newListBuilder(bs.length);
int i = 0;
while (as.hasNext() && i < bs.length) {
target.add(tuple(as.next(), bs[i++]));
}
return mlist(target);
}
@Override
public <B> ListMonadic<Tuple<A, B>> zip(Iterator<B> bs) {
final List<Tuple<A, B>> target = newListBuilder();
while (as.hasNext() && bs.hasNext()) {
target.add(tuple(as.next(), bs.next()));
}
return mlist(target);
}
@Override
public Iterator<A> iterator() {
return as;
}
@Override
public ListMonadic<A> sort(Comparator<A> c) {
throw new UnsupportedOperationException();
}
@Override
public ListMonadic<A> reverse() {
throw new UnsupportedOperationException();
}
@Override
public String mkString(String sep) {
return Collections.mkString(toList(as), sep);
}
@Override
public List<A> value() {
return java.util.Collections.unmodifiableList(toList(as));
}
};
}
/** Constructor function optimized for iterators. */
public static <A> IteratorMonadic<A> mlazy(final Iterator<A> as) {
return new IteratorMonadic<A>() {
@Override
public <B> IteratorMonadic<B> fmap(final Function<A, B> f) {
return mlazy(new Iter<B>() {
@Override
public boolean hasNext() {
return as.hasNext();
}
@Override
public B next() {
return f.apply(as.next());
}
});
}
@Override
public <B> IteratorMonadic<B> mapIndex(final Function2<A, Integer, B> f) {
return mlazy(new Iter<B>() {
private int i = 0;
@Override
public boolean hasNext() {
return as.hasNext();
}
@Override
public B next() {
return f.apply(as.next(), i++);
}
});
}
@Override
public <B> IteratorMonadic<B> bind(final Function<A, Iterator<B>> f) {
return mlazy(new Iter<B>() {
@Override
public boolean hasNext() {
return step.hasNext() || step().hasNext();
}
@Override
public B next() {
if (step.hasNext()) {
return step.next();
} else {
return step().next();
}
}
// iterator state management
private Iterator<B> step = Monadics.emptyIter();
private Iterator<B> step() {
while (!step.hasNext() && as.hasNext()) {
step = f.apply(as.next());
}
return step;
}
});
}
@Override
public boolean exists(Function<A, Boolean> p) {
for (A a : forc(as)) {
if (p.apply(a))
return true;
}
return false;
}
@Override
public IteratorMonadic<A> filter(final Function<A, Boolean> p) {
return mlazy(new Iter<A>() {
private A next = null;
@Override
public boolean hasNext() {
if (next != null) {
return true;
} else {
for (A a : forc(as)) {
if (p.apply(a)) {
next = a;
return true;
}
}
return false;
}
}
@Override
public A next() {
try {
if (next != null || hasNext()) {
return next;
} else {
throw new NoSuchElementException();
}
} finally {
next = null;
}
}
});
}
@Override
public <B> B fold(B zero, Function2<B, A, B> f) {
throw new UnsupportedOperationException();
}
@Override
public A reduce(Function2<A, A, A> f) {
throw new UnsupportedOperationException();
}
@Override
public IteratorMonadic<A> take(final int n) {
return mlazy(new Iter<A>() {
private int count = 0;
@Override
public boolean hasNext() {
return count < n && as.hasNext();
}
@Override
public A next() {
if (count < n) {
count++;
return as.next();
} else {
throw new NoSuchElementException();
}
}
});
}
@Override
public IteratorMonadic<A> each(final Function<A, Void> e) {
return mlazy(new Iter<A>() {
@Override
public boolean hasNext() {
return as.hasNext();
}
@Override
public A next() {
final A a = as.next();
e.apply(a);
return a;
}
});
}
@Override
public IteratorMonadic<A> eachIndex(final Function2<A, Integer, Void> e) {
return mlazy(new Iter<A>() {
private int i = 0;
@Override
public boolean hasNext() {
return as.hasNext();
}
@Override
public A next() {
final A a = as.next();
e.apply(a, i++);
return a;
}
});
}
@Override
public Option<A> next() {
return as.hasNext() ? some(as.next()) : Option.<A>none();
}
@Override
public Iterator<A> iterator() {
return as;
}
@Override
public Iterator<A> value() {
return as;
}
@Override
public List<A> eval() {
return toList(as);
}
};
}
/** Constructor function optimized for lists. */
public static <A> IteratorMonadic<A> mlazy(final List<A> as) {
return mlazy(as.iterator());
}
/** Constructor function. */
public static <A> IteratorMonadic<A> mlazy(final Iterable<A> as) {
return mlazy(as.iterator());
}
/** Constructor function optimized for arrays. */
public static <A> IteratorMonadic<A> mlazy(A... as) {
return mlazy(iterator(as));
}
private abstract static class Iter<A> implements Iterator<A> {
@Override
public final void remove() {
throw new UnsupportedOperationException();
}
}
private static <A> Iterator<A> emptyIter() {
return new Iter<A>() {
@Override
public boolean hasNext() {
return false;
}
@Override
public A next() {
throw new NoSuchElementException();
}
};
}
}