com.jnape.palatable.lambda.functor.builtin.State Maven / Gradle / Ivy
Show all versions of lambda Show documentation
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 super Tuple2, ? 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 super S, ? extends S> fn) {
return state(s -> run(fn.apply(s)));
}
/**
* {@inheritDoc}
*/
@Override
public State local(Fn1 super S, ? extends S> fn) {
return state(s -> run(fn.apply(s)));
}
/**
* {@inheritDoc}
*/
@Override
public State> listens(Fn1 super S, ? extends B> fn) {
return state(s -> run(s).biMapL(both(id(), constantly(fn.apply(s)))));
}
/**
* {@inheritDoc}
*/
@Override
public State censor(Fn1 super S, ? extends S> fn) {
return local(fn);
}
/**
* {@inheritDoc}
*/
@Override
public State flatMap(Fn1 super A, ? extends Monad>> 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 super A, ? extends B> 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 extends Applicative, 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 super A, ? extends MonadRec, 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 super S, ? extends A> 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 super S, ? extends S> 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 super S, ? extends Tuple2> 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));
}
};
}
}