commonMain.arrow.optics.Lens.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.identity
import arrow.typeclasses.Monoid
import kotlin.jvm.JvmStatic
/**
* [Lens] is a type alias for [PLens] which fixes the type arguments
* and restricts the [PLens] to monomorphic updates.
*/
public typealias Lens = PLens
/**
* A [Lens] (or Functional Reference) is an optic that can focus into a structure for
* getting, setting or modifying the focus (target).
*
* A (polymorphic) [PLens] is useful when setting or modifying a value for a constructed type
* i.e. PLens, Pair, Double, String>
*
* A [PLens] can be seen as a pair of functions:
* - `get: (S) -> A` meaning we can focus into an `S` and extract an `A`
* - `set: (B) -> (S) -> T` meaning we can focus into an `S` and set a value `B` for a target `A` and obtain a modified source `T`
*
* @param S the source of a [PLens]
* @param T the modified source of a [PLens]
* @param A the focus of a [PLens]
* @param B the modified focus of a [PLens]
*/
public interface PLens : Getter, POptional, PSetter,
PTraversal, PEvery {
override fun get(source: S): A
override fun set(source: S, focus: B): T
override fun getOrModify(source: S): Either =
Either.Right(get(source))
override fun foldMap(M: Monoid, source: S, map: (focus: A) -> R): R =
map(get(source))
/**
* Join two [PLens] with the same focus in [A]
*/
public infix fun choice(other: PLens): PLens, Either, A, B> = PLens(
{ ss -> ss.fold(this::get, other::get) },
{ ss, b -> ss.mapLeft { s -> set(s, b) }.map { s -> other.set(s, b) } }
)
/**
* Pair two disjoint [PLens]
*/
public infix fun split(other: PLens): PLens, Pair, Pair, Pair> =
PLens(
{ (s, c) -> get(s) to other.get(c) },
{ (s, s1), (b, b1) -> set(s, b) to other.set(s1, b1) }
)
/**
* Create a product of the [PLens] and a type [C]
*/
override fun first(): PLens, Pair, Pair, Pair> = PLens(
{ (s, c) -> get(s) to c },
{ (s, _), (b, c) -> set(s, b) to c }
)
/**
* Create a product of a type [C] and the [PLens]
*/
override fun second(): PLens, Pair, Pair, Pair> = PLens(
{ (c, s) -> c to get(s) },
{ (_, s), (c, b) -> c to set(s, b) }
)
/**
* Compose a [PLens] with another [PLens]
*/
public infix fun compose(other: PLens): PLens = Lens(
{ a -> other.get(get(a)) },
{ s, c -> set(s, other.set(get(s), c)) }
)
public operator fun plus(other: PLens): PLens =
this compose other
public companion object {
public fun id(): PIso = PIso.id()
/**
* [PLens] that takes either [S] or [S] and strips the choice of [S].
*/
public fun codiagonal(): Lens, S> = Lens(
get = { it.fold(::identity, ::identity) },
set = { s, b -> s.mapLeft { b }.map { b } }
)
/**
* Invoke operator overload to create a [PLens] of type `S` with target `A`.
* Can also be used to construct [Lens]
*/
public operator fun invoke(get: (S) -> A, set: (S, B) -> T): PLens =
object : PLens {
override fun get(source: S): A = get(source)
override fun set(source: S, focus: B): T = set(source, focus)
}
/**
* [Lens] to operate on the head of a [NonEmptyList]
*/
@JvmStatic
public fun nonEmptyListHead(): Lens, A> =
Lens(
get = NonEmptyList::head,
set = { nel, newHead -> NonEmptyList(newHead, nel.tail) }
)
/**
* [Lens] to operate on the tail of a [NonEmptyList]
*/
@JvmStatic
public fun nonEmptyListTail(): Lens, List> =
Lens(
get = NonEmptyList::tail,
set = { nel, newTail -> NonEmptyList(nel.head, newTail) }
)
/**
* [PLens] to focus into the first value of a [Pair]
*/
@JvmStatic
public fun pairPFirst(): PLens, Pair, A, R> =
PLens(
get = { it.first },
set = { (_, b), r -> r to b }
)
/**
* [Lens] to focus into the first value of a [Pair]
*/
@JvmStatic
public fun pairFirst(): Lens, A> =
pairPFirst()
/**
* [PLens] to focus into the second value of a [Pair]
*/
@JvmStatic
public fun pairPSecond(): PLens, Pair, B, R> =
PLens(
get = { it.second },
set = { (a, _), r -> a to r }
)
/**
* [Lens] to focus into the second value of a [Pair]
*/
@JvmStatic
public fun pairSecond(): Lens, B> =
pairPSecond()
/**
* [PLens] to focus into the first value of a [Triple]
*/
@JvmStatic
public fun triplePFirst(): PLens, Triple, A, R> =
PLens(
get = { it.first },
set = { (_, b, c), r -> Triple(r, b, c) }
)
/**
* [Lens] to focus into the first value of a [Triple]
*/
@JvmStatic
public fun tripleFirst(): Lens, A> =
triplePFirst()
/**
* [PLens] to focus into the second value of a [Triple]
*/
@JvmStatic
public fun triplePSecond(): PLens, Triple, B, R> =
PLens(
get = { it.second },
set = { (a, _, c), r -> Triple(a, r, c) }
)
/**
* [Lens] to focus into the second value of a [Triple]
*/
@JvmStatic
public fun tripleSecond(): Lens, B> =
triplePSecond()
/**
* [PLens] to focus into the third value of a [Triple]
*/
@JvmStatic
public fun triplePThird(): PLens, Triple, C, R> =
PLens(
get = { it.third },
set = { (a, b, _), r -> Triple(a, b, r) }
)
/**
* [Lens] to focus into the third value of a [Triple]
*/
@JvmStatic
public fun tripleThird(): Lens, C> =
triplePThird()
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy