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

commonMain.arrow.optics.Setter.kt Maven / Gradle / Ivy

There is a newer version: 2.0.1-alpha.1
Show newest version
package arrow.optics

import arrow.core.Either

/**
 * [Setter] is a type alias for [PSetter] which fixes the type arguments
 * and restricts the [PSetter] to monomorphic updates.
 */
public typealias Setter = PSetter

/**
 * A [Setter] is an optic that allows to see into a structure and set or modify its focus.
 *
 * A (polymorphic) [PSetter] is useful when setting or modifying a value for a constructed type
 * i.e. PSetter, List, Int, String>
 *
 * A [PSetter] is a generalisation of a functor.
 * Functor::map   (fa: Kind, f: (A) -> B): Kind
 * PSetter::modify(s: S,         f: (A) -> B): T
 *
 * @param S the source of a [PSetter]
 * @param T the modified source of a [PSetter]
 * @param A the focus of a [PSetter]
 * @param B the modified focus of a [PSetter]
 */
public fun interface PSetter {

  /**
   * Modify polymorphically the focus of a [PSetter] with a function [map].
   */
  public fun modify(source: S, map: (focus: A) -> B): T

  /**
   * Set polymorphically the focus of a [PSetter] with a value [focus].
   */
  public fun set(source: S, focus: B): T =
    modify(source) { focus }

  /**
   * Lift a function [map]: `(A) -> B to the context of `S`: `(S) -> T`
   */
  public fun lift(map: (focus: A) -> B): (source: S) -> T =
    { s -> modify(s) { map(it) } }

  /**
   * Join two [PSetter] with the same target
   */
  public infix fun  choice(other: PSetter): PSetter, Either, A, B> =
    PSetter { su, f ->
      su.mapLeft { s -> modify(s, f) }.map { u -> other.modify(u, f) }
    }

  /**
   * Compose a [PSetter] with a [PSetter]
   */
  public infix fun  compose(other: PSetter): PSetter =
    PSetter { s, fb -> modify(s) { a -> other.modify(a, fb) } }

  public operator fun  plus(other: PSetter): PSetter =
    this compose other

  public companion object {

    public fun  id(): PSetter =
      PIso.id()

    /**
     * [PSetter] that takes either S or S and strips the choice of S.
     */
    public fun  codiagonal(): Setter, S> =
      Setter { aa, f -> aa.mapLeft(f).map(f) }
  }
}