commonMain.arrow.optics.OptionalGetter.kt Maven / Gradle / Ivy
package arrow.optics
import arrow.core.Either
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
import arrow.core.flatMap
import arrow.core.getOrElse
import arrow.typeclasses.Monoid
import kotlin.jvm.JvmStatic
/**
* [OptionalGetter] is a type alias for [POptionalGetter] which fixes the type arguments
* and restricts the [POptionalGetter] to monomorphic updates.
*/
public typealias OptionalGetter = POptionalGetter
public fun OptionalGetter(getOption: (source: S) -> Option): OptionalGetter =
POptionalGetter { s -> getOption(s).toEither { s } }
/**
* An [OptionalGetter] is an optic that allows into a structure and querying an optional focus.
*
* @param S the source of a [POptional]
* @param T the modified source of a [POptional]
* @param A the focus of a [POptional]
*/
public interface POptionalGetter: Fold {
/**
* Get the focus of an [OptionalGetter] or return the original value while allowing the type to change if it does not match
*/
public fun getOrModify(source: S): Either
/**
* Get the focus of an [OptionalGetter] or `null` if the is not there
*/
public fun getOrNull(source: S): A? =
getOrModify(source).getOrNull()
override fun foldMap(M: Monoid, source: S, map: (focus: A) -> R): R =
getOrModify(source).map(map).getOrElse { M.empty() }
/**
* Join two [POptionalGetter] with the same focus
*/
public infix fun choice(other: POptionalGetter): POptionalGetter, Either, A> =
POptionalGetter { sources ->
sources.fold(
{ leftSource ->
getOrModify(leftSource).mapLeft { Either.Left(it) }
},
{ rightSource ->
other.getOrModify(rightSource).mapLeft { Either.Right(it) }
}
)
}
/**
* Create a product of the [POptionalGetter] and a type [C]
*/
public fun first(): POptionalGetter, Pair, Pair> =
POptionalGetter { (source, c) -> getOrModify(source).mapLeft { Pair(it, c) }.map { Pair(it, c) } }
/**
* Create a product of a type [C] and the [POptionalGetter]
*/
public fun second(): POptionalGetter, Pair, Pair> =
POptionalGetter { (c, s) -> getOrModify(s).mapLeft { c to it }.map { c to it } }
/**
* Compose a [POptionalGetter] with a [POptionalGetter]
*/
public infix fun compose(other: POptionalGetter): POptionalGetter =
POptionalGetter { source ->
getOrModify(source).flatMap { a ->
other.getOrModify(a)
}
}
public operator fun plus(other: POptionalGetter): POptionalGetter =
this compose other
public companion object {
/**
* Invoke operator overload to create an [OptionalGetter] of type `S` with focus `A`.
*/
public operator fun invoke(
getOrModify: (source: S) -> Either
): POptionalGetter = object : POptionalGetter {
override fun getOrModify(source: S): Either = getOrModify(source)
}
public fun id(): PIso = PIso.id()
/**
* [OptionalGetter] to itself if it satisfies the predicate.
*
* Select all the elements which satisfy the predicate.
*
* ```kotlin
* import arrow.optics.Traversal
* import arrow.optics.Optional
*
* val positiveNumbers = Traversal.list() compose OptionalGetter.filter { it >= 0 }
*
* positiveNumbers.getAll(listOf(1, 2, -3, 4, -5)) == listOf(1, 2, 4)
* ```
*/
@JvmStatic
public fun filter(predicate: (A) -> Boolean): OptionalGetter =
OptionalGetter(
getOption = { if (predicate(it)) Some(it) else None }
)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy