
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)
© 2015 - 2025 Weber Informatics LLC | Privacy Policy