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-2020, 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 java.util.Arrays;

import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Function2;
import com.github.tonivade.purefun.Higher1;
import com.github.tonivade.purefun.HigherKind;
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.Sealed;
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.MonadDefer;

@Sealed
@HigherKind
public interface Stream {

  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);

  Stream filter(Matcher1 matcher);
  Stream takeWhile(Matcher1 matcher);
  Stream dropWhile(Matcher1 matcher);

  default Stream filterNot(Matcher1 matcher) {
    return filter(matcher.negate());
  }

   Stream collect(PartialFunction1 partial);
   Higher1 foldLeft(R begin, Function2 combinator);
   Higher1 foldRight(Higher1 begin, Function2, Higher1> combinator);

   Stream map(Function1 map);
   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(), Sequence::append);
  }

  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);
  }

  StreamModule getModule();

  static  StreamOf of(MonadDefer monad) {
    return () -> monad;
  }

  interface StreamOf {

    MonadDefer monadDefer();

    default  Stream empty() {
      return new Nil<>(monadDefer());
    }

    @SuppressWarnings("unchecked")
    default  Stream of(T... values) {
      return from(Arrays.stream(values));
    }

    default  Stream pure(T value) {
      return eval(monadDefer().pure(value));
    }

    default  Stream cons(T head, Stream tail) {
      return pure(head).concat(tail);
    }

    default  Stream suspend(Producer> lazy) {
      return new Suspend<>(monadDefer(), monadDefer().defer(lazy.map(monadDefer()::>pure)));
    }

    default  Stream eval(Higher1 value) {
      return new Cons<>(monadDefer(), 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(monadDefer().pure(a)));
    }

    default  Stream unfold(S seed, Function1>> function) {
      return suspend(() -> StreamModule.unfold(this, seed, function));
    }

    default  Stream iterate(T seed, Operator1 generator) {
      return cons(seed, suspend(() -> iterate(generator.apply(seed), generator)));
    }

    default  Stream iterate(Producer generator) {
      return unfold(unit(), unit -> Option.of(generator).map(next -> Tuple.of(next, unit)));
    }

    default  Stream zipWith(Stream s1, Stream s2, Function2 combinator) {
      return new Suspend<>(monadDefer(), monadDefer().defer(
        () -> monadDefer().map2(s1.split(), s2.split(),
          (op1, op2) -> {
            Option> result = StreamModule.map2(op1, op2,
              (t1, t2) -> {
                Higher1 head = monadDefer().map2(t1.get1(), t2.get1(), combinator);
                Stream tail = zipWith(t1.get2(), t2.get2(), combinator);
                return new Cons<>(monadDefer(), head, tail);
              });
            return 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<>(monadDefer(), monadDefer().defer(
        () -> monadDefer().map2(s1.split(), s2.split(),
          (opt1, opt2) -> {
            Option> 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<>(monadDefer(), head, tail);
              });
            return result.getOrElse(this::empty);
          })
        ));
    }
  }
}

interface StreamModule {

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

  static  Stream unfold(Stream.StreamOf streamOf, S seed,
                                                    Function1>> function) {
    return function.apply(seed)
      .map(tuple -> streamOf.cons(tuple.get1(), streamOf.suspend(() -> unfold(streamOf, tuple.get2(), function))))
      .getOrElse(streamOf::empty);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy