arrow.data.Kleisli.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of arrow-data Show documentation
Show all versions of arrow-data Show documentation
Functional Datatypes and abstractions for Kotlin inspired by Cats.
package arrow.data
import arrow.*
import arrow.core.Either
import arrow.core.Tuple2
import arrow.typeclasses.*
/**
* Alias that represents an arrow from [D] to a monadic value `HK`
*/
typealias KleisliFun = (D) -> HK
/**
* [Kleisli] represents an arrow from [D] to a monadic value `HK`.
*
* @param F the context of the result.
* @param D the dependency or environment we depend on.
* @param A resulting type of the computation.
* @property run the arrow from [D] to `HK`.
*/
@higherkind
class Kleisli private constructor(val run: KleisliFun, dummy: Unit = Unit) : KleisliKind, KleisliKindedJ {
/**
* Apply a function `(A) -> B` that operates within the [Kleisli] context.
*
* @param ff function with the [Kleisli] context.
* @param AF [Applicative] for the context [F].
*/
fun ap(ff: KleisliKind B>, AF: Applicative): Kleisli =
Kleisli { AF.ap(run(it), ff.ev().run(it)) }
/**
* Map the end of the arrow [A] to [B] given a function [f].
*
* @param f the function to apply.
* @param FF [Functor] for the context [F].
*/
fun map(f: (A) -> B, FF: Functor): Kleisli = Kleisli { a -> FF.map(run(a)) { f(it) } }
/**
* FlatMap the end of the arrow [A] to another [Kleisli] arrow for the same start [D] and context [F].
*
* @param f the function to flatmap.
* @param MF [Monad] for the context [F].
*/
fun flatMap(f: (A) -> Kleisli, MF: Monad): Kleisli =
Kleisli { d ->
MF.flatMap(run(d)) { a -> f(a).run(d) }
}
/**
* Zip with another [Kleisli] arrow.
*
* @param o other [Kleisli] to zip with.
* @param MF [Monad] for the context [F].
*/
fun zip(o: Kleisli, MF: Monad): Kleisli> =
flatMap({ a ->
o.map({ b -> Tuple2(a, b) }, MF)
}, MF)
/**
* Compose this arrow with another function to transform the input of the arrow.
*
* @param f function that transforms new arrow head [DD] to [D].
*/
fun local(f: (DD) -> D): Kleisli = Kleisli { dd -> run(f(dd)) }
/**
* Compose with another [Kleisli].
*
* @param o other [Kleisli] to compose with.
* @param MF [Monad] for the context [F].
*/
fun andThen(f: Kleisli, MF: Monad): Kleisli = andThen(f.run, MF)
/**
* Compose with a function to transform the output of the [Kleisli] arrow.
*
* @param f the function to apply.
* @param MF [Monad] for the context [F].
*/
fun andThen(f: (A) -> HK, MF: Monad): Kleisli = Kleisli { MF.flatMap(run(it), f) }
/**
* Set the end of the arrow to `HK` after running the computation.
*
* @param fb the new end of the arrow.
* @param MF [Monad] for the context [F].
*/
fun andThen(fb: HK, MF: Monad): Kleisli = andThen({ fb }, MF)
/**
* Handle error within context of [F] given a [MonadError] is defined for [F].
*
* @param f function to handle error.
* @param ME [MonadError] for the context [F].
*/
fun handleErrorWith(f: (E) -> KleisliKind, ME: MonadError): Kleisli = Kleisli {
ME.handleErrorWith(run(it), { e: E -> f(e).ev().run(it) })
}
companion object {
/**
* Constructor to create `Kleisli` given a [KleisliFun].
*
* @param run the arrow from [D] to a monadic value `HK`
*/
operator fun invoke(run: KleisliFun): Kleisli = Kleisli(run, 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) -> KleisliKind>, MF: Monad): Kleisli =
Kleisli { b -> MF.tailRecM(a, { f(it).ev().run(b) }) }
/**
* Create an arrow for a value of [A].
*
* @param x value of [A].
* @param AF [Applicative] for context [F].
*/
inline fun pure(x: A, AF: Applicative = applicative()): Kleisli = Kleisli { _ -> AF.pure(x) }
/**
* Ask an arrow from [D] to [D].
*
* @param AF [Applicative] for context [F].
*/
inline fun ask(AF: Applicative = applicative()): Kleisli = Kleisli { AF.pure(it) }
/**
* Raise an error [E].
* @param ME [MonadError] for context [F].
*/
fun raiseError(e: E, ME: MonadError): Kleisli = Kleisli { ME.raiseError(e) }
}
}
/**
* Flatten nested [Kleisli] arrows.
*
* @param MF [Monad] for the context [F].
*/
inline fun Kleisli>.flatten(MF: Monad): Kleisli = flatMap({ it }, MF)
/**
* Syntax for constructing a [Kleisli]
*
* @receiver [KleisliFun] a function that represents computation dependent on [D] with the result in context [F].
*/
fun KleisliFun.kleisli(): Kleisli = Kleisli(this)
/**
* Alias that represents a computation that has a dependency on [D].
*/
typealias ReaderTFun = KleisliFun
/**
* Alias ReaderTHK for [KleisliHK]
*
* @see KleisliHK
*/
typealias ReaderTHK = KleisliHK
/**
* Alias ReaderTKind for [KleisliKind]
*
* @see KleisliKind
*/
typealias ReaderTKind = KleisliKind
/**
* Alias to partially apply type parameter [F] and [D] to [ReaderT].
*
* @see KleisliKindPartial
*/
typealias ReaderTKindPartial = KleisliKindPartial
/**
* [Reader] represents a computation that has a dependency on [D] with a result within context [F].
* `ReaderT` is the monad transfomer variant of [Reader] and an alias for `Kleisli`.
*
* @param F the context of the result.
* @param D the dependency or environment we depend on.
* @param A resulting type of the computation.
* @see Kleisli
*/
typealias ReaderT = Kleisli
/**
* Syntax for constructing a [ReaderT]
*
* @receiver [ReaderTFun] a function that represents computation dependent on [D] with the result in context [F].
*/
fun ReaderTFun.readerT(): ReaderT = ReaderT(this)
© 2015 - 2025 Weber Informatics LLC | Privacy Policy