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

com.github.tonivade.purefun.monad.State 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.monad;

import static com.github.tonivade.purefun.Unit.unit;
import static com.github.tonivade.purefun.data.ImmutableList.empty;

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.Operator1;
import com.github.tonivade.purefun.Tuple2;
import com.github.tonivade.purefun.Unit;
import com.github.tonivade.purefun.data.Sequence;

@HigherKind
@FunctionalInterface
public non-sealed interface State extends StateOf, Bindable, A> {

  Tuple2 run(S state);

  @Override
  default  State map(Function1 mapper) {
    return flatMap(value -> pure(mapper.apply(value)));
  }

  @Override
  default  State flatMap(Function1, ? extends R>> mapper) {
    return state -> {
      Tuple2 run = run(state);
      State narrowK = mapper.andThen(StateOf::narrowK).apply(run.get2());
      return narrowK.run(run.get1());
    };
  }

  default A eval(S state) {
    return run(state).get2();
  }

  static  State state(Function1> runState) {
    return runState::apply;
  }

  static  State pure(A value) {
    return state -> Tuple2.of(state, value);
  }

  static  State get() {
    return state -> Tuple2.of(state, state);
  }

  static  State set(S value) {
    return state -> Tuple2.of(value, unit());
  }

  static  State modify(Operator1 mapper) {
    return state -> Tuple2.of(mapper.apply(state), unit());
  }

  static  State inspect(Function1 mapper) {
    return state -> Tuple2.of(state, mapper.apply(state));
  }

  static  State> traverse(Sequence> states) {
    return states.foldLeft(pure(empty()),
        (State>sa, State sb) -> map2(sa, sb, Sequence::append));
  }

  static  State map2(State sa, State sb,
                                       Function2 mapper) {
    return sa.flatMap(a -> sb.map(b -> mapper.curried().apply(a).apply(b)));
  }
}