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

com.jnape.palatable.lambda.functor.builtin.State Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
package com.jnape.palatable.lambda.functor.builtin;

import com.jnape.palatable.lambda.adt.Unit;
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.recursion.RecursiveResult;
import com.jnape.palatable.lambda.functions.specialized.Pure;
import com.jnape.palatable.lambda.functor.Applicative;
import com.jnape.palatable.lambda.monad.Monad;
import com.jnape.palatable.lambda.monad.MonadReader;
import com.jnape.palatable.lambda.monad.MonadRec;
import com.jnape.palatable.lambda.monad.MonadWriter;
import com.jnape.palatable.lambda.monad.transformer.builtin.StateT;

import static com.jnape.palatable.lambda.adt.Unit.UNIT;
import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
import static com.jnape.palatable.lambda.functions.Fn1.fn1;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Both.both;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
import static com.jnape.palatable.lambda.functions.recursion.Trampoline.trampoline;
import static com.jnape.palatable.lambda.monad.transformer.builtin.StateT.stateT;

/**
 * The state {@link Monad}, useful for iteratively building up state and state-contextualized result.
 * 

* For more information, read about the * state monad. * * @param the state type * @param the result type */ public final class State implements MonadRec>, MonadReader>, MonadWriter> { private final StateT, A> stateFn; private State(StateT, A> stateFn) { this.stateFn = stateFn; } /** * Run the stateful computation, returning a {@link Tuple2} of the result and the final state. * * @param s the initial state * @return a {@link Tuple2} of the result and the final state. */ public Tuple2 run(S s) { return stateFn.>>runStateT(s).runIdentity(); } /** * Run the stateful computation, returning the result. * * @param s the initial state * @return the result */ public A eval(S s) { return run(s)._1(); } /** * Run the stateful computation, returning the final state. * * @param s the initial state * @return the final state */ public S exec(S s) { return run(s)._2(); } /** * Map both the result and the final state to a new result and final state. * * @param fn the mapping function * @param the new state type * @return the mapped {@link State} */ public State mapState(Fn1, ? extends Tuple2> fn) { return state(s -> fn.apply(run(s))); } /** * Map the final state to a new final state using the provided function. * * @param fn the state-mapping function * @return the mapped {@link State} */ public State withState(Fn1 fn) { return state(s -> run(fn.apply(s))); } /** * {@inheritDoc} */ @Override public State local(Fn1 fn) { return state(s -> run(fn.apply(s))); } /** * {@inheritDoc} */ @Override public State> listens(Fn1 fn) { return state(s -> run(s).biMapL(both(id(), constantly(fn.apply(s))))); } /** * {@inheritDoc} */ @Override public State censor(Fn1 fn) { return local(fn); } /** * {@inheritDoc} */ @Override public State flatMap(Fn1>> f) { return state(s -> run(s).into((a, s2) -> f.apply(a).>coerce().run(s2))); } /** * {@inheritDoc} */ @Override public State pure(B b) { return state(s -> tuple(b, s)); } /** * {@inheritDoc} */ @Override public State fmap(Fn1 fn) { return MonadRec.super.fmap(fn).coerce(); } /** * {@inheritDoc} */ @Override public State zip(Applicative, State> appFn) { return MonadRec.super.zip(appFn).coerce(); } /** * {@inheritDoc} */ @Override public Lazy> lazyZip( Lazy, State>> lazyAppFn) { return MonadRec.super.lazyZip(lazyAppFn).fmap(Monad>::coerce); } /** * {@inheritDoc} */ @Override public State discardR(Applicative> appB) { return MonadRec.super.discardR(appB).coerce(); } /** * {@inheritDoc} */ @Override public State discardL(Applicative> appB) { return MonadRec.super.discardL(appB).coerce(); } /** * {@inheritDoc} */ @Override public State trampolineM(Fn1, State>> fn) { return state(fn1(this::run).fmap(trampoline(into((a, s) -> fn.apply(a) .>>coerce().run(s) .into((aOrB, s_) -> aOrB.biMap(a_ -> tuple(a_, s_), b -> tuple(b, s_))))))); } /** * Create a {@link State} that simply returns back the initial state as both the result and the final state * * @param the state and result type * @return the new {@link State} instance */ @SuppressWarnings("RedundantTypeArguments") public static State get() { return state(Tuple2::fill); } /** * Create a {@link State} that ignores its initial state, returning a {@link Unit} result and s as its * final state. * * @param s the final state * @param the state type * @return the new {@link State} instance */ public static State put(S s) { return modify(constantly(s)); } /** * Create a {@link State} that maps its initial state into its result, but leaves the initial state unchanged. * * @param fn the mapping function * @param the state type * @param the result type * @return the new {@link State} instance */ public static State gets(Fn1 fn) { return state(both(fn, id())); } /** * Create a {@link State} that maps its initial state into its final state, returning a {@link Unit} result type. * * @param fn the mapping function * @param the state type * @return the new {@link State} instance */ public static State modify(Fn1 fn) { return state(both(constantly(UNIT), fn)); } /** * Create a {@link State} that returns a as its result and its initial state as its final state. * * @param a the result * @param the state type * @param the result type * @return the new {@link State} instance */ public static State state(A a) { return gets(constantly(a)); } /** * Create a {@link State} from stateFn, a function that maps an initial state into a result and a final * state. * * @param stateFn the state function * @param the state type * @param the result type * @return the new {@link State} instance */ public static State state(Fn1> stateFn) { return new State<>(stateT(s -> new Identity<>(stateFn.apply(s)))); } /** * The canonical {@link Pure} instance for {@link State}. * * @param the state type * @return the {@link Pure} instance */ public static Pure> pureState() { return new Pure>() { @Override public State checkedApply(A a) throws Throwable { return state(s -> tuple(a, s)); } }; } }