Module Loko

Loko — Lower-Kinded Optics

Typically one either refers to the library primitives through the Loko module name or, as done in this documentation, uses an abbreviation such as

module L = Loko

to keep code more succinct.

Loko uses ordinary function composition to compose optics and for convenience, the Infix submodule

open L.Infix

provides the % operator for that purpose.

Auxiliary abstractions

NOTE: The abstractions in this section serve auxiliary roles for the definition of optics.

module Infix : sig ... end

Infix operators for convenient use of optics.

module Iterator : sig ... end

Imperative iterator.

module Builder : sig ... end

Imperative builder.

Optic types

type (-'S, +'T) pipe

The pipe type constructor hides most of the implementation details of optics and basically represents a function that takes values of type 'S as input and produces values of type 'T as output.

type (-'S, +'F, -'G, +'T) t = ('F, 'G) pipe -> ('S, 'T) pipe

Optics are functions over pipes and are composable with normal function composition.

type ('S, 'F, 'G, 'T) optic := ('S, 'F, 'G, 'T) t

Erased type alias for optic.

Optic constructors

val iso : ('S -> 'F) -> ('G -> 'T) -> ('S, 'F, 'G, 'T) t

Defines a new invertible isomorphism from a pair of conversion functions.

# let plus n = L.iso (( + ) n) (Fun.flip ( - ) n)
val plus : int -> (int, int, int, int) L.t = <fun>

# L.view (plus 1) 2
- : int = 3

# L.review (plus 2) 3
- : int = 1

# L.view (L.re (plus 2)) 3
- : int = 1
val lens : ('S -> 'F) -> ('G -> 'S -> 'T) -> ('S, 'F, 'G, 'T) t

Defines a new lens from a getter and setter.

val partial_lens : ('S -> 'F option) -> ('G -> 'S -> 'T) -> ('S -> 'T) -> ('S, 'F, 'G, 'T) t

Defines a new partial lens from a getter, setter, and remover.

val getter : ('S -> 'F) -> ('S, 'F, 'G, 'S) t

Defines a new getter.

val fold_lens : (('S, 'E, 'G, 'T) t -> 'S -> 'F) -> ('S, 'E, 'G, 'T) t -> ('S, 'F, 'G, 'T) t

fold_lens fold traversal defines a lens out of a suitable fold and a traversal.

val prism : ('S -> ('T, 'F) Stdlib.Either.t) -> ('G -> 'T) -> ('S, 'F, 'G, 'T) t

Defines a new invertible prism from a constructor-destructor pair.

# let ends_with suffix =
    L.prism
      (fun string ->
        if Filename.check_suffix string suffix then
          Right (Filename.chop_suffix string suffix)
        else Left string)
      (fun prefix -> prefix ^ suffix)
val ends_with : string -> (string, string, string, string) L.t = <fun>

# L.view (ends_with ".ml") "Loko.ml"
- : string = "Loko"

# L.set (ends_with ".ml") "Loco" "Loko.ml"
- : string = "Loco.ml"

# L.can_view (ends_with ".ml") "Loko.mli"
- : bool = false

# L.set (ends_with ".ml") "Loco" "Loko.mli"
- : string = "Loko.mli"
val traversal : ('S, 'F) Iterator.t -> ('G, 'T) Builder.t -> ('S, 'F, 'G, 'T) t

Defines a new traversal from an iterator-builder pair.

val branch'2 : ('F1, 'F, 'G, 'G1) t -> ('F2, 'F, 'G, 'G2) t -> ('F1 * 'F2, 'F, 'G, 'G1 * 'G2) t

Defines a new traversal over the elements of a 2-tuple.

val branch'3 : ('F1, 'F, 'G, 'G1) t -> ('F2, 'F, 'G, 'G2) t -> ('F3, 'F, 'G, 'G3) t -> ('F1 * 'F2 * 'F3, 'F, 'G, 'G1 * 'G2 * 'G3) t

Defines a new traversal over the elements of a 3-tuple.

val branch'4 : ('F1, 'F, 'G, 'G1) t -> ('F2, 'F, 'G, 'G2) t -> ('F3, 'F, 'G, 'G3) t -> ('F4, 'F, 'G, 'G4) t -> ('F1 * 'F2 * 'F3 * 'F4, 'F, 'G, 'G1 * 'G2 * 'G3 * 'G4) t

Defines a new traversal over the elements of a 4-tuple.

Viewing a focus

val can_view : ('S, 'F, 'G, 'T) t -> 'S -> bool

Determines whether the optic has a focus on the data.

val view : ('S, 'F, 'G, 'T) t -> 'S -> 'F

Extracts the first focus of the optic on the data. Raises when there is no focus.

val view_opt : ('S, 'F, 'G, 'T) t -> 'S -> 'F option

Attempts to extract the first focus of the optic on the data.

Reviewing through invertible optics

val can_review : ('S, 'F, 'G, 'T) t -> 'G -> bool

Determines whether the optic has an inverse focus on the data.

val review : ('S, 'F, 'G, 'T) t -> 'G -> 'T

Views through isomorphism in inverse direction. Raises when the optic is not reversible or doesn't have inverse focus on the data.

val review_opt : ('S, 'F, 'G, 'T) t -> 'G -> 'T option

Attempts to extract the inverse focus of the optic on the data. WARNING: This will raise when the optic is not reversible.

Mapping over focuses

val can_over : ('S, 'F, 'G, 'T) t -> ('F -> 'G) -> 'S -> bool

Determines whether the focuses of the optic on the data can be updated.

val over : ('S, 'F, 'G, 'T) t -> ('F -> 'G) -> 'S -> 'T

Updates the focuses of the optic on the data. Raises when cannot.

val over_default : ('S, 'F, 'G, 'S) t -> ('F -> 'G) -> 'S -> 'S

Updates the focuses of the optic on the data. Returns input when cannot.

val over_opt : ('S, 'F, 'G, 'T) t -> ('F -> 'G) -> 'S -> 'T option

Attempts to update the focuses of the optic on the data.

Setting focuses

val can_set : ('S, 'F, 'G, 'T) t -> 'G -> 'S -> bool

Determines whether the focuses of the optic on the data can be set.

val set : ('S, 'F, 'G, 'T) t -> 'G -> 'S -> 'T

Sets the focuses of the optic on the data. Raises when cannot.

val set_default : ('S, 'F, 'G, 'S) t -> 'G -> 'S -> 'S

Sets the focuses of the optic on the data. Returns input when cannot.

val set_opt : ('S, 'F, 'G, 'T) t -> 'G -> 'S -> 'T option

Attempts to set the focuses of the optic on the data.

Removing focuses

val can_remove : ('S, 'F, 'G, 'T) t -> 'S -> bool

Determines whether the focuses of the optic on the data can be removed.

val remove : ('S, 'F, 'G, 'T) t -> 'S -> 'T

Removes the focuses of the optic on the data. Raises when cannot.

val remove_default : ('S, 'F, 'G, 'S) t -> 'S -> 'S

Removes the focuses of the optic on the data. Returns input when cannot.

val remove_opt : ('S, 'F, 'G, 'T) t -> 'S -> 'T option

Attempts to remove the focuses of the optic on the data.

Dispersing focuses

val disperse : ('S, 'F, 'G, 'T) t -> 'G array -> 'S -> 'T

Injects array elements to the focuses or removes them. Raises when cannot.

val disperse_keep : ('S, 'F, 'F, 'T) t -> 'F array -> 'S -> 'T

Injects array elements to the focuses or keeps them. Raises when cannot.

val parts_of : ('S, 'F, 'G, 'T) t -> ('S, 'F array, 'G array, 'T) t

Converts an optic to a lens focusing on an array of the focuses.

val parts_of_keep : ('S, 'F, 'F, 'T) t -> ('S, 'F array, 'F array, 'T) t

Converts an optic to a lens focusing on an array of the focuses.

Transforms

val transform : ('S, 'F, 'F, 'T) t -> 'S -> 'T

transform optic data is equivalent to over optic Fun.id data.

Transform ops

val remove_op : ('S, 'F, 'G, 'T) t

Signal removal of current focus.

val over_op : ('S -> 'T) -> ('S, 'F, 'G, 'T) t

Update focus with given function.

val set_op : 'T -> ('S, 'F, 'G, 'T) t

Update focus with given value.

Combinators

val choose : ('S -> ('S, 'F, 'G, 'T) t) -> ('S, 'F, 'G, 'T) t

A non-isomorphism computed from the focus.

val if_else : ('S -> bool) -> ('S, 'F, 'G, 'T) t -> ('S, 'F, 'G, 'T) t -> ('S, 'F, 'G, 'T) t

A choice between two non-isomorphisms depending on the focus.

val or_else : ('S, 'F, 'G, 'T) t -> ('S, 'F, 'G, 'T) t -> ('S, 'F, 'G, 'T) t

or_else secondary primary acts like the primary when it can view and otherwise like secondary.

val tuple'2 : ('S, 'L1, 'L2, 'S) t -> ('S, 'R1, 'R2, 'T) t -> ('S, 'L1 * 'R1, 'L2 * 'R2, 'T) t

A lens focusing on a pair of focuses. Given optics should be separable lenses.

val and_then : ('U, 'F, 'G, 'T) t -> ('S, 'F, 'G, 'U) t -> ('S, 'F, 'G, 'T) t

op_1 |> and_then op_2 first performs with op_1 and then op_2.

# L.transform (L.over_op (( + ) 2) |> L.and_then (L.over_op (( * ) 3))) 1
- : int = 9

# L.transform
    ((L.fst |> L.and_then L.snd) % L.over_op Int.to_string)
    (1, 2)
- : string * string = ("1", "2")

Conditionals

The cond_of, case, and otherwise combinators provide an alternative syntax for conditional optics. In general,

cond_of traversal
@@ case predicate_1 optic_1
@@ case predicate_2 optic_2
   ...
@@ case predicate_n optic_n
@@ otherwise optic

is equivalent to

if_else (exists predicate_1 traversal) t_1
@@ if_else (exists predicate_2 traversal) optic_2
   ...
@@ if_else (exists predicate_n traversal) optic_n
@@ optic

using if_else and exists.

val cond_of : ('S, 'C, 'C, 'U) t -> (('S, 'C, 'C, 'U) t -> 'K) -> 'K

cond_of traversal starts a conditional optic definition where the following cases use the traversal to extract targets for their predicates.

val case : ('C -> bool) -> ('S, 'F, 'G, 'T) t -> (('S, 'C, 'C, 'U) t -> ('S, 'F, 'G, 'T) t) -> ('S, 'C, 'C, 'U) t -> ('S, 'F, 'G, 'T) t

case predicate optic continues a conditional optic definition with given predicate and consequent optic.

val otherwise : ('S, 'F, 'G, 'T) t -> ('S, 'C, 'C, 'U) t -> ('S, 'F, 'G, 'T) t

otherwise optic ends a conditional optic definition and provides the default optic to use when none of the cases match the focus.

Primitives

val zero : ('S, 'F, 'G, 'S) t

An optic that never has a focus.

Removal

val removed_if : ('G -> bool) -> ('F, 'F, 'G, 'G) t

An identity like optic that signals removal when written value matches predicate.

val as_removed : 'G -> ('F, 'F, 'G, 'G) t

A prism like optic that signals removal when written with equal value.

val removed_as : 'G -> ('F, 'F, 'G, 'G) t

A prism like optic that maps a removed focus to given value when written.

val removed_as_none : ('F, 'F, 'G, 'G option) t

A prism like optic that maps a removed focus to None when written.

val none_as_removed : ('F, 'F, 'G option, 'G) t

A prism like optic that maps None to a removed focus when written.

val removed_option : ('F option, 'F, 'G, 'G) t

An optic that peels away Some on read and removes None on write.

Prisms

val accept : ('F -> bool) -> ('F, 'F, 'F, 'F) t

A prism with a focus only when it passes the given predicate.

# L.remove
    (L.List.elems % L.accept (fun x -> 4 < x))
    [ 5; 3; 1; 6; 4; 1 ]
- : int list = [3; 1; 4; 1]
val reject : ('F -> bool) -> ('F, 'F, 'F, 'F) t

A prism with a focus except when it passes the given predicate.

# L.remove
    (L.List.elems % L.reject (fun x -> x < 5))
    [ 5; 3; 1; 6; 4; 1 ]
- : int list = [3; 1; 4; 1]

Isomorphisms

val id : ('S, 'S, 'T, 'T) t

The identity isomorphism.

val re : ('S, 'F, 'G, 'T) t -> ('G, 'T, 'S, 'F) t

Inverts the given optic. Raises when not given an invertible optic.

val reread : ('S -> 'F) -> ('S, 'F, 'T, 'T) t

An isomorphism that maps the focus with given function when read.

val rewrite : ('G -> 'T) -> ('S, 'S, 'G, 'T) t

An isomorphism that maps the focus with given function when written.

val normalize : ('S -> 'F) -> ('S, 'F, 'S, 'F) t

An isomorphism that maps the focus with given function in both ways.

val subset : ('S -> bool) -> ('S, 'S option, 'T, 'T) t

An isomorphism between values and values that pass the predicate.

val to_default : 'F -> ('F option, 'F, 'G, 'G) t

An isomorphism that maps None to the given default value when read.

val of_default : 'G -> ('F, 'F, 'G, 'G option) t

An isomorphism that maps the given default value to None when written.

val default : 'F -> ('F option, 'F, 'F, 'F option) t

An isomorphism that maps None to the given default value when read and maps the default value to None when written.

val is_or : falsy:'F -> truthy:'F -> ('F, bool, bool, 'F) t

An isomorphism between given values and booleans. Only truthy maps to true when read.

val apply_at : ('S, 'Si, 'Fi, 'F) t -> ('G, 'Gi, 'Ti, 'T) t -> ('Si, 'Fi, 'Gi, 'Ti) t -> ('S, 'F, 'G, 'T) t

TODO

val iterate : ('S, 'S, 'T, 'T) t -> ('S, 'S, 'T, 'T) t

TODO

Side-effects

val before : ('S -> unit) -> ('S, 'S, 'T, 'T) t

An identity like optic that performs a given side-effect when viewed.

val after : ?on_removed:(unit -> unit) -> ('T -> unit) -> ('S, 'S, 'T, 'T) t

An identity like optic that performs given side-effects when written.

Folds

val fold : 'R -> ('R -> 'F -> 'R) -> ('S, 'F, 'G, 'T) t -> 'S -> 'R

Folds over the focuses of the optic on the data.

# L.fold 0 ( + ) L.List.elems [ 3; 1; 4 ]
- : int = 8
val iter : ('S, 'F, 'G, 'T) t -> ('F -> unit) -> 'S -> unit

Iterates over the focuses of the optic on the data.

val count : ('S, 'F, 'G, 'T) t -> 'S -> int

Counts the number of focuses the optic has on the data.

val collect_with : ('F, 'FS) Builder.t -> ('S, 'F, 'G, 'T) t -> 'S -> 'FS

Collects values of the focuses of the optic on the data with the given builder.

val collect : ('S, 'F, 'G, 'T) t -> 'S -> 'F list

Extracts a list of all the focuses of the optic on the data.

val exists : ('F -> bool) -> ('S, 'F, 'F, 'T) t -> 'S -> bool

Determines whether any focus of the optic on the data satisfy the predicate.

val forall : ('F -> bool) -> ('S, 'F, 'F, 'T) t -> 'S -> bool

Determines whether all focuces of the optic on the data satisfy the predicate.

val disjunction : ('S, bool, bool, 'T) t -> 'S -> bool

disjunction optic data is equivalent to exists Fun.id optic data.

val conjunction : ('S, bool, bool, 'T) t -> 'S -> bool

conjunction optic data is equivalent to forall Fun.id optic data.

val concat : string -> ('S, string, 'G, 'T) t -> 'S -> string

Concatenates all of the focuses of the optic on the data with given separator.

Pairs

val fst : ('L1 * 'R, 'L1, 'L2, 'L2 * 'R) t

A lens focusing on the first element of a pair.

val snd : ('L * 'R1, 'R1, 'R2, 'L * 'R2) t

A lens focusing on the second element of a pair.

val iso_pair : ('SL, 'FL, 'GL, 'TL) t -> ('SR, 'FR, 'GR, 'TR) t -> ('SL * 'SR, 'FL * 'FR, 'GL * 'GR, 'TL * 'TR) t

An isomorphism between pairs.

Tuples

val elem_1_of_2 : ('F * 'X2, 'F, 'G, 'G * 'X2) t
val elem_2_of_2 : ('X1 * 'F, 'F, 'G, 'X1 * 'G) t
val elem_1_of_3 : ('F * 'X2 * 'X3, 'F, 'G, 'G * 'X2 * 'X3) t
val elem_2_of_3 : ('X1 * 'F * 'X3, 'F, 'G, 'X1 * 'G * 'X3) t
val elem_3_of_3 : ('X1 * 'X2 * 'F, 'F, 'G, 'X1 * 'X2 * 'G) t
val elem_1_of_4 : ('F * 'X2 * 'X3 * 'X4, 'F, 'G, 'G * 'X2 * 'X3 * 'X4) t
val elem_2_of_4 : ('X1 * 'F * 'X3 * 'X4, 'F, 'G, 'X1 * 'G * 'X3 * 'X4) t
val elem_3_of_4 : ('X1 * 'X2 * 'F * 'X4, 'F, 'G, 'X1 * 'X2 * 'G * 'X4) t
val elem_4_of_4 : ('X1 * 'X2 * 'X3 * 'F, 'F, 'G, 'X1 * 'X2 * 'X3 * 'G) t

Numerics

val truncate : (float, int, int, float) t

An isomorphism between integers and floats.

Stdlib data types

module Array : sig ... end
module Either : sig ... end
module List : sig ... end
module Map : sig ... end
module Option : sig ... end
module Result : sig ... end
module String : sig ... end