All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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>> stream) {
    return new Suspend<>(monad, defer, defer.defer(stream));
  }
}

final class Nil implements Stream {

  private final Monad monad;
  private final Defer defer;

  Nil(Monad monad, Defer defer) {
    this.monad = requireNonNull(monad);
    this.defer = requireNonNull(defer);
  }

  @Override
  public Higher1> headOption() {
    return monad.pure(Option.none());
  }

  @Override
  public Higher1, Stream>>> split() {
    return monad.pure(Option.none());
  }

  @Override
  public Stream take(int n) {
    return this;
  }

  @Override
  public Stream drop(int n) {
    return this;
  }

  @Override
  public Stream filter(Matcher1 matcher) {
    return this;
  }

  @Override
  public Stream takeWhile(Matcher1 matcher) {
    return this;
  }

  @Override
  public Stream dropWhile(Matcher1 matcher) {
    return this;
  }

  @Override
  public Stream concat(Stream other) {
    return other;
  }

  @Override
  public Stream append(Higher1 other) {
    return new Cons<>(monad, defer, other, this);
  }

  @Override
  public Stream prepend(Higher1 other) {
    return append(other);
  }

  @Override
  public  Stream collect(PartialFunction1 partial) {
    return new Nil<>(monad, defer);
  }

  @Override
  public  Higher1 foldLeft(R begin, Function2 combinator) {
    return monad.pure(begin);
  }

  @Override
  public  Higher1 foldRight(Higher1 begin, Function2, Higher1> combinator) {
    return begin;
  }

  @Override
  public Higher1 exists(Matcher1 matcher) {
    return monad.pure(false);
  }

  @Override
  public Higher1 forall(Matcher1 matcher) {
    return monad.pure(true);
  }

  @Override
  public  Stream map(Function1 map) {
    return new Nil<>(monad, defer);
  }

  @Override
  public  Stream mapEval(Function1> mapper) {
    return new Nil<>(monad, defer);
  }

  @Override
  public  Stream flatMap(Function1> map) {
    return new Nil<>(monad, defer);
  }

  @Override
  public Stream repeat() {
    return this;
  }

  @Override
  public Stream intersperse(Higher1 value) {
    return this;
  }
}

interface StreamModule {
  static  Option map2(Option fa, Option fb, Function2 combiner) {
    return fa.flatMap(a -> fb.map(b -> combiner.apply(a, b)));
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy