
com.github.tonivade.purefun.stream.Stream Maven / Gradle / Ivy
/*
* Copyright (c) 2018-2019, Antonio Gabriel Muñoz Conejo
* Distributed under the terms of the MIT License
*/
package com.github.tonivade.purefun.stream;
import static com.github.tonivade.purefun.Unit.unit;
import static com.github.tonivade.purefun.data.Sequence.asStream;
import static java.util.Objects.requireNonNull;
import java.util.Arrays;
import com.github.tonivade.purefun.Filterable;
import com.github.tonivade.purefun.FlatMap2;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Function2;
import com.github.tonivade.purefun.Higher1;
import com.github.tonivade.purefun.Higher2;
import com.github.tonivade.purefun.Kind;
import com.github.tonivade.purefun.Matcher1;
import com.github.tonivade.purefun.Operator1;
import com.github.tonivade.purefun.PartialFunction1;
import com.github.tonivade.purefun.Producer;
import com.github.tonivade.purefun.Tuple;
import com.github.tonivade.purefun.Tuple2;
import com.github.tonivade.purefun.Unit;
import com.github.tonivade.purefun.data.ImmutableList;
import com.github.tonivade.purefun.data.Sequence;
import com.github.tonivade.purefun.type.Option;
import com.github.tonivade.purefun.typeclasses.Defer;
import com.github.tonivade.purefun.typeclasses.Monad;
public interface Stream extends FlatMap2, Filterable {
final class µ implements Kind {}
default Stream head() {
return take(1);
}
default Stream tail() {
return drop(1);
}
Higher1> headOption();
Higher1, Stream>>> split();
Stream concat(Stream other);
Stream append(Higher1 other);
Stream prepend(Higher1 other);
Stream take(int n);
Stream drop(int n);
@Override
Stream filter(Matcher1 matcher);
Stream takeWhile(Matcher1 matcher);
Stream dropWhile(Matcher1 matcher);
Stream collect(PartialFunction1 partial);
Higher1 foldLeft(R begin, Function2 combinator);
Higher1 foldRight(Higher1 begin, Function2, Higher1> combinator);
@Override
Stream map(Function1 map);
@Override
Stream flatMap(Function1> map);
Stream mapEval(Function1> mapper);
Stream repeat();
Stream intersperse(Higher1 value);
Higher1 exists(Matcher1 matcher);
Higher1 forall(Matcher1 matcher);
default Stream through(Function1, Stream> function) {
return function.apply(this);
}
default Higher1> asSequence() {
return foldLeft(ImmutableList.empty(), (acc, a) -> acc.append(a));
}
default Higher1 asString() {
return foldLeft("", (acc, a) -> acc + a);
}
default Higher1 drain() {
return foldLeft(unit(), (acc, a) -> acc);
}
default Stream andThen(Higher1 next) {
return mapEval(ignore -> next);
}
static StreamOf of(Monad monad, Defer defer) {
return new StreamOf() {
@Override
public Monad monad() { return monad; }
@Override
public Defer defer() { return defer; }
};
}
static Stream narrowK(Higher1, T> hkt) {
return (Stream) hkt;
}
static Stream narrowK(Higher2 hkt) {
return (Stream) hkt;
}
interface StreamOf {
Monad monad();
Defer defer();
default Stream empty() {
return new Nil<>(monad(), defer());
}
@SuppressWarnings("unchecked")
default Stream of(T... values) {
return from(Arrays.stream(values));
}
default Stream pure(T value) {
return eval(monad().pure(value));
}
default Stream suspend(Producer> lazy) {
return new Suspend<>(monad(), defer(), defer().defer(lazy.andThen(monad()::pure)));
}
default Stream eval(Higher1 value) {
return new Cons<>(monad(), defer(), value, empty());
}
default Stream from(Iterable iterable) {
return from(asStream(iterable.iterator()));
}
default Stream from(java.util.stream.Stream stream) {
return from(ImmutableList.from(stream));
}
default Stream from(Sequence sequence) {
return sequence.foldLeft(empty(), (acc, a) -> acc.append(monad().pure(a)));
}
default Stream iterate(T seed, Operator1 generator) {
return new Cons<>(monad(), defer(), monad().pure(seed),
suspend(() -> iterate(generator.apply(seed), generator)));
}
default Stream iterate(Producer generator) {
return new Cons<>(monad(), defer(), monad().pure(generator.get()),
suspend(() -> iterate(generator)));
}
default Stream zipWith(Stream s1, Stream s2, Function2 combinator) {
return new Suspend<>(monad(), defer(), defer().defer(
() -> monad().map2(s1.split(), s2.split(),
(op1, op2) -> {
Higher1> result = StreamModule.map2(op1, op2,
(t1, t2) -> {
Higher1 head = monad().map2(t1.get1(), t2.get1(), combinator);
Stream tail = zipWith(t1.get2(), t2.get2(), combinator);
return new Cons<>(monad(), defer(), head, tail);
});
return Option.narrowK(result).getOrElse(this::empty);
})
));
}
default Stream> zip(Stream s1, Stream s2) {
return zipWith(s1, s2, Tuple2::of);
}
default Stream> zipWithIndex(Stream stream) {
return zip(stream, iterate(0, x -> x + 1));
}
default Stream merge(Stream s1, Stream s2) {
return new Suspend<>(monad(), defer(), defer().defer(
() -> monad().map2(s1.split(), s2.split(),
(opt1, opt2) -> {
Higher1> result = StreamModule.map2(opt1, opt2,
(t1, t2) -> {
Higher1 head = t1.get1();
Stream tail = eval(t2.get1()).concat(merge(t1.get2(), t2.get2()));
return new Cons<>(monad(), defer(), head, tail);
});
return Option.narrowK(result).getOrElse(this::empty);
})
));
}
}
}
final class Cons implements Stream {
private final Monad monad;
private final Defer defer;
private final Higher1 head;
private final Stream tail;
Cons(Monad monad, Defer defer, Higher1 head, Stream tail) {
this.monad = requireNonNull(monad);
this.defer = requireNonNull(defer);
this.head = requireNonNull(head);
this.tail = requireNonNull(tail);
}
@Override
public Higher1> headOption() {
return monad.map(head, Option::some);
}
@Override
public Higher1, Stream>>> split() {
return monad.pure(Option.some(Tuple.of(head, tail)));
}
@Override
public Stream concat(Stream other) {
return suspend(() -> cons(head, tail.concat(other)));
}
@Override
public Stream append(Higher1 other) {
return suspend(() -> cons(head, tail.append(other)));
}
@Override
public Stream prepend(Higher1 other) {
return suspend(() -> cons(other, tail.prepend(head)));
}
@Override
public Stream take(int n) {
return n > 0 ? suspend(() -> cons(head, tail.take(n - 1))) : empty();
}
@Override
public Stream drop(int n) {
return n > 0 ? suspend(() -> tail.drop(n - 1)) : this;
}
@Override
public Stream takeWhile(Matcher1 matcher) {
return suspendF(() -> monad.map(head,
t -> matcher.match(t) ? cons(head, tail.takeWhile(matcher)) : empty()));
}
@Override
public Stream dropWhile(Matcher1 matcher) {
return suspendF(() ->
monad.map(head, t -> matcher.match(t) ?
tail.dropWhile(matcher) : this));
}
@Override
public Stream filter(Matcher1 matcher) {
return suspendF(() ->
monad.map(head, t -> matcher.match(t) ?
cons(head, tail.filter(matcher)) : tail.filter(matcher)));
}
@Override
public Stream collect(PartialFunction1 partial) {
return suspendF(() ->
monad.map(head, t -> partial.isDefinedAt(t) ?
cons(monad.map(head, partial::apply), tail.collect(partial)) : tail.collect(partial)));
}
@Override
public Higher1 foldLeft(R begin, Function2 combinator) {
return monad.flatMap(head, h -> tail.foldLeft(combinator.apply(begin, h), combinator));
}
@Override
public Higher1 foldRight(Higher1 begin, Function2, Higher1> combinator) {
return monad.flatMap(head, h -> tail.foldRight(combinator.apply(h, begin), combinator));
}
@Override
public Higher1 exists(Matcher1 matcher) {
return foldRight(monad.pure(false), (t, acc) -> matcher.match(t) ? monad.pure(true) : acc);
}
@Override
public Higher1 forall(Matcher1 matcher) {
return foldRight(monad.pure(true), (t, acc) -> matcher.match(t) ? acc : monad.pure(false));
}
@Override
public Stream map(Function1 map) {
return suspend(() -> cons(monad.map(head, map), suspend(() -> tail.map(map))));
}
@Override
public Stream mapEval(Function1> mapper) {
return suspend(() -> cons(monad.flatMap(head, mapper), suspend(() -> tail.mapEval(mapper))));
}
@Override
public Stream flatMap(Function1> map) {
return suspendF(() ->
monad.map(
monad.map(head, map.andThen(Stream::narrowK)::apply),
s -> s.concat(tail.flatMap(map))));
}
@Override
public Stream repeat() {
return concat(suspend(this::repeat));
}
@Override
public Stream intersperse(Higher1 value) {
return suspend(() -> cons(head, suspend(() -> cons(value, tail.intersperse(value)))));
}
private Stream cons(Higher1 head, Stream tail) {
return new Cons<>(monad, defer, head, tail);
}
private Stream suspend(Producer> stream) {
return suspendF(stream.andThen(monad::pure));
}
private Stream suspendF(Producer>> stream) {
return new Suspend<>(monad, defer, defer.defer(stream));
}
private Stream empty() {
return new Nil<>(monad, defer);
}
}
final class Suspend implements Stream {
private final Monad monad;
private final Defer defer;
private final Higher1> evalStream;
Suspend(Monad monad, Defer defer, Higher1> stream) {
this.monad = requireNonNull(monad);
this.defer = requireNonNull(defer);
this.evalStream = requireNonNull(stream);
}
@Override
public Higher1> headOption() {
return monad.flatMap(evalStream, Stream::headOption);
}
@Override
public Higher1, Stream>>> split() {
return monad.flatMap(evalStream, Stream::split);
}
@Override
public Stream concat(Stream other) {
return lazyMap(s -> s.concat(other));
}
@Override
public Stream append(Higher1 other) {
return lazyMap(s -> s.append(other));
}
@Override
public Stream prepend(Higher1 other) {
return lazyMap(s -> s.prepend(other));
}
@Override
public Stream take(int n) {
return lazyMap(s -> s.take(n));
}
@Override
public Stream drop(int n) {
return lazyMap(s -> s.drop(n));
}
@Override
public Stream takeWhile(Matcher1 matcher) {
return lazyMap(s -> s.takeWhile(matcher));
}
@Override
public Stream dropWhile(Matcher1 matcher) {
return lazyMap(s -> s.dropWhile(matcher));
}
@Override
public Stream filter(Matcher1 matcher) {
return lazyMap(s -> s.filter(matcher));
}
@Override
public Stream collect(PartialFunction1 partial) {
return lazyMap(s -> s.collect(partial));
}
@Override
public Higher1 foldLeft(R begin, Function2 combinator) {
return monad.flatMap(evalStream, s -> s.foldLeft(begin, combinator));
}
@Override
public Higher1 foldRight(Higher1 begin, Function2, Higher1> combinator) {
return monad.flatMap(evalStream, s -> s.foldRight(begin, combinator));
}
@Override
public Higher1 exists(Matcher1 matcher) {
return monad.flatMap(evalStream, s -> s.exists(matcher));
}
@Override
public Higher1 forall(Matcher1 matcher) {
return monad.flatMap(evalStream, s -> s.forall(matcher));
}
@Override
public Stream map(Function1 mapper) {
return lazyMap(s -> s.map(mapper));
}
@Override
public Stream mapEval(Function1> mapper) {
return suspend(() -> monad.map(evalStream, s -> s.mapEval(mapper)));
}
@Override
public Stream flatMap(Function1> map) {
return lazyMap(s -> s.flatMap(map));
}
@Override
public Stream repeat() {
return lazyMap(Stream::repeat);
}
@Override
public Stream intersperse(Higher1 value) {
return lazyMap(s -> s.intersperse(value));
}
private Stream lazyMap(Function1, Stream> mapper) {
return suspend(() -> monad.map(evalStream, mapper));
}
private Stream suspend(Producer