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

com.github.tonivade.purefun.stream.PureStream Maven / Gradle / Ivy

/*
 * Copyright (c) 2018-2023, 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.HigherKind;
import com.github.tonivade.purefun.Kind;
import com.github.tonivade.purefun.Bindable;
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.Witness;
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;

@HigherKind
public sealed interface PureStream extends PureStreamOf, Bindable, T> permits Cons, Suspend, Nil {

  default PureStream head() {
    return take(1);
  }

  default PureStream tail() {
    return drop(1);
  }

  Kind> headOption();
  Kind, PureStream>>> split();

  PureStream concat(PureStream other);
  PureStream append(Kind other);
  PureStream prepend(Kind other);

  PureStream take(int n);
  PureStream drop(int n);

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

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

   PureStream collect(PartialFunction1 partial);
   Kind foldLeft(R begin, Function2 combinator);
   Kind foldRight(Kind begin, 
      Function2, ? extends Kind> combinator);

  @Override
   PureStream map(Function1 map);
  @Override
   PureStream flatMap(Function1, ? extends R>> map);
  @Override
  default  PureStream andThen(Kind, ? extends R> next) {
    return flatMap(ignore -> next);
  }

   PureStream mapEval(Function1> mapper);

  PureStream repeat();
  PureStream intersperse(Kind value);

  Kind exists(Matcher1 matcher);
  Kind forall(Matcher1 matcher);

  default  PureStream through(Function1, PureStream> function) {
    return function.apply(this);
  }

  default Kind> asSequence() {
    return foldLeft(ImmutableList.empty(), Sequence::append);
  }

  default Kind asString() {
    return foldLeft("", (acc, a) -> acc + a);
  }

  default Kind drain() {
    return foldLeft(unit(), (acc, a) -> acc);
  }

  default  PureStream mapReplace(Kind next) {
    return mapEval(ignore -> next);
  }

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

  interface StreamOf {

    MonadDefer monadDefer();

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

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

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

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

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

    default  PureStream eval(Kind value) {
      return new Cons<>(monadDefer(), Kind.narrowK(value), empty());
    }

    default  PureStream from(Iterable iterable) {
      return from(asStream(iterable.iterator()));
    }

    default  PureStream from(java.util.stream.Stream stream) {
      return from(ImmutableList.from(stream));
    }

    default  PureStream from(Sequence sequence) {
      return sequence.foldLeft(empty(), (acc, a) -> acc.append(monadDefer().pure(a)));
    }

    default  PureStream unfold(S seed, Function1>> function) {
      return suspend(() -> doUnfold(seed, function));
    }

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

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

    default  PureStream zipWith(PureStream s1, PureStream s2, 
        Function2 combinator) {
      return new Suspend<>(monadDefer(), monadDefer().defer(
        () -> monadDefer().mapN(s1.split(), s2.split(),
          (op1, op2) -> {
            Option> result = Option.map2(op1, op2,
              (t1, t2) -> {
                Kind head = monadDefer().mapN(t1.get1(), t2.get1(), combinator);
                PureStream tail = zipWith(t1.get2(), t2.get2(), combinator);
                return new Cons<>(monadDefer(), head, tail);
              });
            return result.getOrElse(this::empty);
          })
        ));
    }

    default  PureStream> zip(PureStream s1, PureStream s2) {
      return zipWith(s1, s2, Tuple2::of);
    }

    default  PureStream> zipWithIndex(PureStream stream) {
      return zip(stream, iterate(0, x -> x + 1));
    }

    // TODO: generics
    default  PureStream merge(PureStream s1, PureStream s2) {
      return new Suspend<>(monadDefer(), monadDefer().defer(
        () -> monadDefer().mapN(s1.split(), s2.split(),
          (opt1, opt2) -> {
            Option> result = Option.map2(opt1, opt2,
              (t1, t2) -> {
                Kind head = t1.get1();
                PureStream tail = eval(t2.get1()).concat(merge(t1.get2(), t2.get2()));
                return new Cons<>(monadDefer(), head, tail);
              });
            return result.getOrElse(this::empty);
          })
        ));
    }

    private  PureStream doUnfold(S seed, Function1>> function) {
      return function.apply(seed)
        .map(tuple -> tuple.applyTo((t, s) -> cons(t, suspend(() -> doUnfold(s, function)))))
        .getOrElse(this::empty);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy