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

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

There is a newer version: 0.8.2
Show newest version
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)