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

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

package arrow.data

import arrow.*
import arrow.core.*
import arrow.typeclasses.Applicative
import arrow.typeclasses.Functor
import arrow.typeclasses.Monad
import arrow.typeclasses.applicative

/**
 * [OptionT]`` is a light wrapper on an `F<`[Option]`>` with some
 * convenient methods for working with this nested structure.
 *
 * It may also be said that [OptionT] is a monad transformer for [Option].
 */
@higherkind data class OptionT(val value: HK>) : OptionTKind, OptionTKindedJ {

    companion object {

        operator fun  invoke(value: HK>): OptionT = OptionT(value)

        inline fun  pure(a: A, AF: Applicative = applicative()): OptionT = OptionT(AF.pure(Some(a)))

        inline fun  none(AF: Applicative = applicative()): OptionT = OptionT(AF.pure(None))

        inline fun  fromOption(value: Option, AF: Applicative = applicative()): OptionT =
                OptionT(AF.pure(value))

        fun  tailRecM(a: A, f: (A) -> OptionTKind>, MF: Monad): OptionT =
                OptionT(MF.tailRecM(a, {
                    MF.map(f(it).ev().value, {
                        it.fold({
                            Right>(None)
                        }, {
                            it.map { Some(it) }
                        })
                    })
                }))

    }

    inline fun  fold(crossinline default: () -> B, crossinline f: (A) -> B, FF: Functor): HK = FF.map(value, { option -> option.fold(default, f) })

    inline fun  cata(crossinline default: () -> B, crossinline f: (A) -> B, FF: Functor): HK = fold(default, f, FF)

    fun  ap(ff: OptionTKind B>, MF: Monad): OptionT = ff.ev().flatMap({ f -> map(f, MF) }, MF)

    inline fun  flatMap(crossinline f: (A) -> OptionT, MF: Monad): OptionT = flatMapF({ it -> f(it).value }, MF)

    inline fun  flatMapF(crossinline f: (A) -> HK>, MF: Monad): OptionT =
            OptionT(MF.flatMap(value, { option -> option.fold({ MF.pure(None) }, f) }))

    fun  liftF(fa: HK, FF: Functor): OptionT = OptionT(FF.map(fa, { Some(it) }))

    inline fun  semiflatMap(crossinline f: (A) -> HK, MF: Monad): OptionT = flatMap({ option -> liftF(f(option), MF) }, MF)

    inline fun  map(crossinline f: (A) -> B, FF: Functor): OptionT = OptionT(FF.map(value, { it.map(f) }))

    fun getOrElse(default: () -> A, FF: Functor): HK = FF.map(value, { it.getOrElse(default) })

    inline fun getOrElseF(crossinline default: () -> HK, MF: Monad): HK = MF.flatMap(value, { it.fold(default, { MF.pure(it) }) })

    inline fun filter(crossinline p: (A) -> Boolean, FF: Functor): OptionT = OptionT(FF.map(value, { it.filter(p) }))

    inline fun forall(crossinline p: (A) -> Boolean, FF: Functor): HK = FF.map(value, { it.forall(p) })

    fun isDefined(FF: Functor): HK = FF.map(value, { it.isDefined() })

    fun isEmpty(FF: Functor): HK = FF.map(value, { it.isEmpty() })

    inline fun orElse(crossinline default: () -> OptionT, MF: Monad): OptionT = orElseF({ default().value }, MF)

    inline fun orElseF(crossinline default: () -> HK>, MF: Monad): OptionT =
            OptionT(MF.flatMap(value) {
                when (it) {
                    is Some -> MF.pure(it)
                    is None -> default()
                }
            })

    inline fun  transform(crossinline f: (Option) -> Option, FF: Functor): OptionT = OptionT(FF.map(value, { f(it) }))

    inline fun  subflatMap(crossinline f: (A) -> Option, FF: Functor): OptionT = transform({ it.flatMap(f) }, FF)

    fun  toLeft(default: () -> R, FF: Functor): EitherT =
            EitherT(cata({ Right(default()) }, { Left(it) }, FF))

    fun  toRight(default: () -> L, FF: Functor): EitherT =
            EitherT(cata({ Left(default()) }, { Right(it) }, FF))
}

inline fun  OptionT.mapFilter(crossinline f: (A) -> Option, FF: Functor): OptionT =
        OptionT(FF.map(value, { it.flatMap(f) }))

fun  OptionTKind.value(): HK> = this.ev().value