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

arrow.data.StateT.kt Maven / Gradle / Ivy

package arrow.data

import arrow.*
import arrow.core.*
import arrow.typeclasses.*

/**
 * Alias that represent stateful computation of the form `(S) -> Tuple2` with a result in certain context `F`.
 */
typealias StateTFun = (S) -> HK>

/**
 * Alias that represents wrapped stateful computation in context `F`.
 */
typealias StateTFunKind = HK>

/**
 * Run the stateful computation within the context `F`.
 *
 * @param MF [Monad] for the context [F]
 * @param s initial state to run stateful computation
 */
fun  StateTKind.runM(MF: Monad, initial: S): HK> = ev().run(initial, MF)

/**
 * Run the stateful computation within the context `F`.
 *
 * @param s initial state to run stateful computation
 * @param MF [Monad] for the context [F]
 */
inline fun  StateTKind.runM(initial: S, MF: Monad = monad()): HK> = ev().run(initial, MF)

/**
 * `StateT` is a stateful computation within a context `F` yielding
 * a value of type `A`. i.e. StateT, S, A> = Either>
 *
 * @param F the context that wraps the stateful computation.
 * @param S the state we are preforming computation upon.
 * @param A current value of computation.
 * @param runF the stateful computation that is wrapped and managed by `StateT`
 */
@higherkind
class StateT(
        val runF: StateTFunKind
) : StateTKind, StateTKindedJ {

    companion object {

        inline fun  pure(t: T, MF: Monad = monad()): StateT =
                StateT { s -> MF.pure(s toT t) }

        /**
         * Constructor to create `StateT` given a [StateTFun].
         *
         * @param run the stateful function to wrap with [StateT].
         * @param MF [Monad] for the context [F].
         */
        inline operator fun  invoke(noinline run: StateTFun, MF: Monad = monad()): StateT = StateT(MF.pure(run))

        /**
         * Constructor to create `StateT` given a [StateTFun].
         *
         * @param MF [Monad] for the context [F].
         * @param run the stateful function to wrap with [StateT].
         */
        inline operator fun  invoke(MF: Monad = monad(), noinline run: StateTFun): StateT = StateT(MF.pure(run))

        /**
         * Alias for constructor [StateT].
         *
         * @param runF the function to wrap within [StateT].
         */
        fun  invokeF(runF: StateTFunKind): StateT = StateT(runF)

        /**
         * Lift a value of type `A` into `StateT`.
         *
         * @param MF [Monad] for the context [F].
         * @param fa the value to lift.
         */
        fun  lift(MF: Monad, fa: HK): StateT = StateT(MF.pure({ s -> MF.map(fa, { a -> Tuple2(s, a) }) }))

        /**
         * Return input without modifying it.
         *
         * @param AF [Applicative] for the context [F].
         */
        fun  get(AF: Applicative): StateT = StateT(AF.pure({ s -> AF.pure(Tuple2(s, s)) }))

        /**
         * Inspect a value of the state [S] with [f] `(S) -> T` without modifying the state.
         *
         *
         *
         * @param AF [Applicative] for the context [F].
         * @param f the function applied to inspect [T] from [S].
         */
        fun  inspect(AF: Applicative, f: (S) -> T): StateT = StateT(AF.pure({ s -> AF.pure(Tuple2(s, f(s))) }))

        /**
         * Modify the state with [f] `(S) -> S` and return [Unit].
         *
         * @param AF [Applicative] for the context [F].
         * @param f the modify function to apply.
         */
        fun  modify(AF: Applicative, f: (S) -> S): StateT = StateT(AF.pure({ s -> AF.map(AF.pure(f(s))) { Tuple2(it, Unit) } }))

        /**
         * Modify the state with an [Applicative] function [f] `(S) -> HK` and return [Unit].
         *
         * @param AF [Applicative] for the context [F].
         * @param f the modify function to apply.
         */
        fun  modifyF(AF: Applicative, f: (S) -> HK): StateT = StateT(AF.pure({ s -> AF.map(f(s)) { Tuple2(it, Unit) } }))

        /**
         * Set the state to a value [s] and return [Unit].
         *
         * @param AF [Applicative] for the context [F].
         * @param s value to set.
         */
        fun  set(AF: Applicative, s: S): StateT = StateT(AF.pure({ _ -> AF.pure(Tuple2(s, Unit)) }))

        /**
         * Set the state to a value [s] of type `HK` and return [Unit].
         *
         * @param AF [Applicative] for the context [F].
         * @param s value to set.
         */
        fun  setF(AF: Applicative, s: HK): StateT = StateT(AF.pure({ _ -> AF.map(s) { Tuple2(it, Unit) } }))

        /**
         * Tail recursive function that keeps calling [f]  until [arrow.Either.Right] is returned.
         *
         * @param a initial value to start running recursive call to [f]
         * @param f function that is called recusively until [arrow.Either.Right] is returned.
         * @param MF [Monad] for the context [F].
         */
        fun  tailRecM(a: A, f: (A) -> HK, Either>, MF: Monad): StateT =
                StateT(MF.pure({ s: S ->
                    MF.tailRecM(Tuple2(s, a), { (s, a0) ->
                        MF.map(f(a0).runM(MF, s)) { (s, ab) ->
                            ab.bimap({ a1 -> Tuple2(s, a1) }, { b -> Tuple2(s, b) })
                        }
                    })
                }))
    }

    /**
     * Map current value [A] given a function [f].
     *
     * @param f the function to apply.
     * @param FF [Functor] for the context [F].
     */
    fun  map(f: (A) -> B, FF: Functor): StateT = transform({ (s, a) -> Tuple2(s, f(a)) }, FF)

    /**
     * Combine with another [StateT] of same context [F] and state [S].
     *
     * @param sb other state with value of type `B`.
     * @param f the function to apply.
     * @param MF [Monad] for the context [F].
     */
    fun  map2(sb: StateT, fn: (A, B) -> Z, MF: Monad): StateT =
            invokeF(MF.map2(runF, sb.runF) { (ssa, ssb) ->
                ssa.andThen { fsa ->
                    MF.flatMap(fsa) { (s, a) ->
                        MF.map(ssb(s)) { (s, b) -> Tuple2(s, fn(a, b)) }
                    }
                }
            })

    /**
     * Controlled combination of [StateT] that is of same context [F] and state [S] using [Eval].
     *
     * @param sb other state with value of type `B`.
     * @param f the function to apply.
     * @param MF [Monad] for the context [F].
     */
    fun  map2Eval(sb: Eval>, fn: (A, B) -> Z, MF: Monad): Eval> =
            MF.map2Eval(runF, sb.map { it.runF }) { (ssa, ssb) ->
                ssa.andThen { fsa ->
                    MF.flatMap(fsa) { (s, a) ->
                        MF.map(ssb((s))) { (s, b) -> Tuple2(s, fn(a, b)) }
                    }
                }
            }.map { invokeF(it) }

    /**
     * Apply a function `(S) -> B` that operates within the [StateT] context.
     *
     * @param ff function with the [StateT] context.
     * @param MF [Monad] for the context [F].
     */
    fun  ap(ff: StateTKind B>, MF: Monad): StateT =
            ff.ev().map2(this, { f, a -> f(a) }, MF)

    /**
     * Create a product of the value types of [StateT].
     *
     * @param sb other stateful computation.
     * @param MF [Monad] for the context [F].
     */
    fun  product(sb: StateT, MF: Monad): StateT> = map2(sb, { a, b -> Tuple2(a, b) }, MF)

    /**
     * Map the value [A] to another [StateT] object for the same state [S] and context [F] and flatten the structure.
     *
     * @param fas the function to apply.
     * @param MF [Monad] for the context [F].
     */
    fun  flatMap(fas: (A) -> StateTKind, MF: Monad): StateT =
            invokeF(
                    MF.map(runF) { sfsa ->
                        sfsa.andThen { fsa ->
                            MF.flatMap(fsa) {
                                fas(it.b).runM(MF, it.a)
                            }
                        }
                    })

    /**
     * Map the value [A] to a arbitrary type [B] that is within the context of [F].
     *
     * @param faf the function to apply.
     * @param MF [Monad] for the context [F].
     */
    fun  flatMapF(faf: (A) -> HK, MF: Monad): StateT =
            invokeF(
                    MF.map(runF) { sfsa ->
                        sfsa.andThen { fsa ->
                            MF.flatMap(fsa) { (s, a) ->
                                MF.map(faf(a)) { b -> Tuple2(s, b) }
                            }
                        }
                    })

    /**
     * Transform the product of state [S] and value [A] to an another product of state [S] and an arbitrary type [B].
     *
     * @param f the function to apply.
     * @param FF [Functor] for the context [F].
     */
    fun  transform(f: (Tuple2) -> Tuple2, FF: Functor): StateT =
            invokeF(
                    FF.map(runF) { sfsa ->
                        sfsa.andThen { fsa ->
                            FF.map(fsa, f)
                        }
                    })

    /**
     * Combine two [StateT] objects using an instance of [SemigroupK] for [F].
     *
     * @param y other [StateT] object to combine.
     * @param MF [Monad] for the context [F].
     * @param SF [SemigroupK] for [F].
     */
    fun combineK(y: StateTKind, MF: Monad, SF: SemigroupK): StateT =
            StateT(MF.pure({ s -> SF.combineK(run(s, MF), y.ev().run(s, MF)) }))

    /**
     * Run the stateful computation within the context `F`.
     *
     * @param s initial state to run stateful computation.
     * @param MF [Monad] for the context [F].
     */
    fun run(initial: S, MF: Monad): HK> = MF.flatMap(runF) { f -> f(initial) }

    /**
     * Run the stateful computation within the context `F` and get the value [A].
     *
     * @param s initial state to run stateful computation.
     * @param MF [Monad] for the context [F].
     */
    fun runA(s: S, MF: Monad): HK = MF.map(run(s, MF)) { it.b }

    /**
     * Run the stateful computation within the context `F` and get the state [S].
     *
     * @param s initial state to run stateful computation.
     * @param MF [Monad] for the context [F].
     */
    fun runS(s: S, MF: Monad): HK = MF.map(run(s, MF)) { it.a }
}

/**
 * Wrap the function with [StateT].
 *
 * @param MF [Monad] for the context [F].
 */
inline fun  StateTFunKind.stateT(MF: Monad = monad()): StateT = StateT(this)

/**
 * Wrap the function with [StateT].
 *
 * @param MF [Monad] for the context [F].
 */
inline fun  StateTFun.stateT(MF: Monad = monad()): StateT = StateT(this, MF)

/**
 * Lift a value of type `A` into `StateT`.
 *
 * @param MF [Monad] for the context [F].
 * @param fa the value to lift.
 */
inline fun  StateT.Companion.lift(fa: HK, MF: Monad = monad()): StateT = lift(MF, fa)

/**
 * Return input without modifying it.
 *
 * @param AF [Applicative] for the context [F].
 */
inline fun  StateT.Companion.get(AF: Applicative = applicative(), dummy: Unit = Unit): StateT = get(AF)

/**
 * Inspect a value of the state [S] with [f] `(S) -> T` without modifying the state.
 *
 * @param AF [Applicative] for the context [F].
 * @param f the function applied to extrat [T] from [S].
 */
inline fun  StateT.Companion.inspect(AF: Applicative = applicative(), dummy: Unit = Unit, crossinline f: (S) -> T): StateT = inspect(AF) { f(it) }

/**
 * Modify the state with [f] `(S) -> S` and return [Unit].
 *
 * @param AF [Applicative] for the context [F].
 * @param f the modify function to apply.
 */
inline fun  StateT.Companion.modify(AF: Applicative = applicative(), dummy: Unit = Unit, crossinline f: (S) -> S): StateT = modify(AF) { f(it) }

/**
 * Modify the state with an [Applicative] function [f] `(S) -> HK` and return [Unit].
 *
 * @param AF [Applicative] for the context [F].
 * @param f the modify function to apply.
 */
inline fun  StateT.Companion.modifyF(AF: Applicative = applicative(), dummy: Unit = Unit, crossinline f: (S) -> HK): StateT = modifyF(AF) { f(it) }

/**
 * Set the state to a value [s] and return [Unit].
 *
 * @param AF [Applicative] for the context [F].
 * @param s value to set.
 */
inline fun  StateT.Companion.set(s: S, AF: Applicative = applicative()): StateT = set(AF, s)

/**
 * Set the state to a value [s] of type `HK` and return [Unit].
 *
 * @param AF [Applicative] for the context [F].
 * @param s value to set.
 */
inline fun  StateT.Companion.set(s: HK, AF: Applicative = applicative()): StateT = setF(AF, s)