commonMain.arrow.optics.Fold.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of arrow-optics-jvm Show documentation
Show all versions of arrow-optics-jvm Show documentation
Functional companion to Kotlin's Standard Library
package arrow.optics
import arrow.core.Either
import arrow.core.NonEmptyList
import arrow.core.Option
import arrow.core.Tuple10
import arrow.core.Tuple4
import arrow.core.Tuple5
import arrow.core.Tuple6
import arrow.core.Tuple7
import arrow.core.Tuple8
import arrow.core.Tuple9
import arrow.core.foldMap
import arrow.core.identity
import arrow.typeclasses.Monoid
import arrow.typeclasses.MonoidDeprecation
import kotlin.jvm.JvmStatic
/**
* A [Fold] is an optic that allows to focus into structure and get multiple results.
*
* [Fold] is a generalisation of an instance of [Foldable] and is implemented in terms of foldMap.
*
* @param S the source of a [Fold]
* @param A the target of a [Fold]
*/
public interface Fold {
/**
* Map each target to a type [R] and combine the results as a fold.
*/
public fun foldMap(empty: R, combine: (R, R) -> R, source: S, map: (focus: A) -> R): R =
foldMap(object : Monoid {
override fun empty(): R = empty
override fun R.combine(b: R): R = combine(this, b)
}, source, map)
/**
* Map each target to a type R and use a Monoid to fold the results
*/
@Deprecated(MonoidDeprecation, ReplaceWith("foldMap(M.empty(), M::combine, source, map)", "arrow.optics.foldMap", "arrow.typeclasses.combine"))
public fun foldMap(M: Monoid, source: S, map: (focus: A) -> R): R
/**
* Calculate the number of targets
*/
public fun size(source: S): Int =
foldMap(0, { x, y -> x + y }, source) { 1 }
/**
* Check if all targets satisfy the predicate
*/
public fun all(source: S, predicate: (focus: A) -> Boolean): Boolean =
foldMap(true, { x, y -> x && y }, source, predicate)
/**
* Returns `true` if at least one focus matches the given [predicate].
*/
public fun any(source: S, predicate: (focus: A) -> Boolean): Boolean =
foldMap(false, { x, y -> x || y }, source, predicate)
/**
* Check if there is no target
*/
public fun isEmpty(source: S): Boolean =
foldMap(true, { _, _ -> false }, source) { false }
/**
* Check if there is at least one target
*/
public fun isNotEmpty(source: S): Boolean =
!isEmpty(source)
/**
* Get the first target or null
*/
public fun firstOrNull(source: S): A? =
EMPTY_VALUE.unbox(foldMap(EMPTY_VALUE, { x, y -> if (x === EMPTY_VALUE) y else x }, source, ::identity))
/**
* Get the last target or null
*/
public fun lastOrNull(source: S): A? =
EMPTY_VALUE.unbox(foldMap(EMPTY_VALUE, { x, y -> if (y != EMPTY_VALUE) y else x }, source, ::identity))
/**
* Fold using the given [empty] element and [combine].
*/
public fun fold(empty: A, combine: (A, A) -> A, source: S): A =
foldMap(empty, combine, source, ::identity)
/**
* Fold using the given [Monoid] instance.
*/
@Deprecated(MonoidDeprecation, ReplaceWith("fold(M.empty(), M::combine, source)", "arrow.optics.fold", "arrow.typeclasses.combine"))
public fun fold(M: Monoid, source: S): A =
foldMap(M, source, ::identity)
/**
* Alias for fold.
*/
@Deprecated("use fold instead", ReplaceWith("fold(M, source)"))
public fun combineAll(M: Monoid, source: S): A =
fold(M, source)
/**
* Get all targets of the [Fold]
*/
public fun getAll(source: S): List =
foldMap(emptyList(), { x, y -> x + y }, source) { listOf(it) }
/**
* Find the first element matching the predicate, if one exists.
*/
public fun findOrNull(source: S, predicate: (focus: A) -> Boolean): A? =
EMPTY_VALUE.unbox(
foldMap(EMPTY_VALUE, { x, y -> if (x == EMPTY_VALUE) y else x }, source) { focus ->
if (predicate(focus)) focus else EMPTY_VALUE
}
)
/**
* Check whether at least one element satisfies the predicate.
*
* If there are no elements, the result is false.
*/
public fun exists(source: S, predicate: (focus: A) -> Boolean): Boolean =
any(source, predicate)
/**
* Join two [Fold] with the same target
*/
public infix fun choice(other: Fold): Fold, A> =
object : Fold, A> {
override fun foldMap(M: Monoid, source: Either, map: (focus: A) -> R): R =
source.fold({ ac -> [email protected](M, ac, map) }, { c -> other.foldMap(M, c, map) })
}
/**
* Create a sum of the [Fold] and a type [C]
*/
public fun left(): Fold, Either> =
object : Fold, Either> {
override fun foldMap(M: Monoid, source: Either, map: (Either) -> R): R =
source.fold(
{ a1: S -> [email protected](M, a1) { b -> map(Either.Left(b)) } },
{ c -> map(Either.Right(c)) })
}
/**
* Create a sum of a type [C] and the [Fold]
*/
public fun right(): Fold, Either> =
object : Fold, Either> {
override fun foldMap(M: Monoid, source: Either, map: (Either) -> R): R =
source.fold({ c -> map(Either.Left(c)) }, { a1 -> [email protected](M, a1) { b -> map(Either.Right(b)) } })
}
/**
* Compose a [Fold] with a [Fold]
*/
public infix fun compose(other: Fold): Fold =
object : Fold {
override fun foldMap(M: Monoid, source: S, map: (focus: C) -> R): R =
[email protected](M, source) { c -> other.foldMap(M, c, map) }
}
public operator fun plus(other: Fold): Fold =
this compose other
public companion object {
public fun id(): Fold =
PIso.id()
/**
* [Fold] that takes either [S] or [S] and strips the choice of [S].
*/
public fun codiagonal(): Fold, S> = object : Fold, S> {
override fun foldMap(M: Monoid, source: Either, map: (S) -> R): R =
source.fold(map, map)
}
/**
* Creates a [Fold] based on a predicate of the source [S]
*/
public fun select(p: (S) -> Boolean): Fold = object : Fold {
override fun foldMap(M: Monoid, source: S, map: (S) -> R): R =
if (p(source)) map(source) else M.empty()
}
/**
* [Fold] that points to nothing
*/
public fun void(): Fold =
POptional.void()
@JvmStatic
public fun iterable(): Fold, A> =
object : Fold, A> {
override fun foldMap(M: Monoid, source: Iterable, map: (focus: A) -> R): R =
source.foldMap(M, map)
}
/**
* [Traversal] for [List] that focuses in each [A] of the source [List].
*/
@JvmStatic
public fun list(): Fold, A> =
Every.list()
/**
* [Traversal] for [Either] that has focus in each [Either.Right].
*
* @return [Traversal] with source [Either] and focus every [Either.Right] of the source.
*/
@JvmStatic
public fun either(): Fold, R> =
Every.either()
@JvmStatic
public fun map(): Fold
© 2015 - 2025 Weber Informatics LLC | Privacy Policy