common

package
v2.3.118 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package optional provides an optic for focusing on values that may not exist.

Overview

Optional is an optic used to zoom inside a product. Unlike the Lens, the element that the Optional focuses on may not exist. An Optional[S, A] represents a relationship between a source type S and a focus type A, where the focus may or may not be present.

Optional Laws

An Optional must satisfy the following laws, which are consistent with other functional programming libraries such as monocle-ts (https://gcanti.github.io/monocle-ts/modules/Optional.ts.html) and the Haskell lens library (https://hackage.haskell.org/package/lens):

  1. GetSet Law (No-op on None): If GetOption(s) returns None, then Set(a)(s) must return s unchanged (no-op). This ensures that attempting to update a value that doesn't exist has no effect.

    Formally: GetOption(s) = None => Set(a)(s) = s

  2. SetGet Law (Get what you Set): If GetOption(s) returns Some(_), then GetOption(Set(a)(s)) must return Some(a). This ensures that after setting a value, you can retrieve it.

    Formally: GetOption(s) = Some(_) => GetOption(Set(a)(s)) = Some(a)

  3. SetSet Law (Last Set Wins): Setting twice is the same as setting once with the final value.

    Formally: Set(b)(Set(a)(s)) = Set(b)(s)

No-op Behavior

A key property of Optional is that updating a value for which GetOption returns None is a no-op. This behavior is implemented through the optionalModify function, which only applies the modification if the optional value exists. When GetOption returns None, the original structure is returned unchanged.

This is consistent with the behavior in:

  • monocle-ts: Optional.modify returns the original value when the optional doesn't match
  • Haskell lens: over and set operations are no-ops when the traversal finds no targets

Example

type Person struct {
    Name string
    Age  int
}

// Create an optional that focuses on non-empty names
nameOptional := MakeOptional(
    func(p Person) option.Option[string] {
        if p.Name != "" {
            return option.Some(p.Name)
        }
        return option.None[string]()
    },
    func(p Person, name string) Person {
        p.Name = name
        return p
    },
)

// When the optional matches, Set updates the value
person1 := Person{Name: "Alice", Age: 30}
updated1 := nameOptional.Set("Bob")(person1)
// updated1.Name == "Bob"

// When the optional doesn't match (Name is empty), Set is a no-op
person2 := Person{Name: "", Age: 30}
updated2 := nameOptional.Set("Bob")(person2)
// updated2 == person2 (unchanged)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsoComposeIso

func IsoComposeIso[S, A, B any](ab Iso[A, B]) func(Iso[S, A]) Iso[S, B]

IsoComposeIso combines two isomorphisms to create a new isomorphism. Given Iso[S, A] and Iso[A, B], creates Iso[S, B]. The resulting isomorphism first applies the outer iso (S → A), then the inner iso (A → B).

Type Parameters:

  • S: The outermost source type
  • A: The intermediate type
  • B: The innermost target type

Parameters:

  • ab: The inner isomorphism (A → B)

Returns:

  • A function that takes the outer isomorphism (S → A) and returns the composed isomorphism (S → B)

Example:

metersToKm := MakeIso(
    func(m float64) float64 { return m / 1000 },
    func(km float64) float64 { return km * 1000 },
)

kmToMiles := MakeIso(
    func(km float64) float64 { return km * 0.621371 },
    func(mi float64) float64 { return mi / 0.621371 },
)

// IsoComposeIso: meters → kilometers → miles
metersToMiles := F.Pipe1(metersToKm, IsoComposeIso[float64](kmToMiles))

miles := metersToMiles.Get(5000)        // ~3.11 miles
meters := metersToMiles.ReverseGet(3.11) // ~5000 meters

func IsoComposeLens

func IsoComposeLens[S, A, B any](ab Lens[A, B]) func(Iso[S, A]) Lens[S, B]

IsoComposeLens combines an isomorphism with a lens to produce a new lens.

Given an isomorphism Iso[S, A] and a lens Lens[A, B], IsoComposeLens creates a Lens[S, B]. Internally the isomorphism is first converted to a Lens[S, A] via IsoAsLens, then composed with the provided lens using LensComposeLens.

This is useful when the outer view of a structure is captured by a total, invertible transformation (an iso) and you want to drill further into the result with a lens — for example, when S is a newtype wrapper around A and you need to focus on a field of A.

The resulting lens satisfies all three lens laws whenever both the isomorphism and the inner lens individually satisfy them.

The composition follows: (sa ∘ ab).Get(s) = ab.Get(sa.Get(s))

Type Parameters:

  • S: Outermost source type — the input to the resulting lens
  • A: Intermediate type — the target of the iso and the source of the inner lens
  • B: Inner focus type — the focus of the resulting lens

Parameters:

  • ab: Lens from A to B (inner lens)

Returns:

  • A function that takes an Iso[S, A] and returns a Lens[S, B]

Example:

type Celsius float64
type Thermometer struct{ Reading Celsius }

// Isomorphism: float64 ↔ Celsius
floatToCelsius := MakeIso(
    func(f float64) Celsius { return Celsius(f) },
    func(c Celsius) float64 { return float64(c) },
)

// Lens: Thermometer → Celsius (focuses on the Reading field)
readingLens := MakeLens(
    func(t Thermometer) Celsius { return t.Reading },
    func(t Thermometer, c Celsius) Thermometer { t.Reading = c; return t },
)

// Compose: float64 → Celsius → Thermometer.Reading (Celsius)
// Result type: Lens[float64, Celsius]
tempLens := F.Pipe1(floatToCelsius, IsoComposeLens[float64](readingLens))

t := Thermometer{Reading: 100}
reading := tempLens.Get(100.0)          // Celsius(100)
updated := tempLens.Set(Celsius(0))(0.0) // Thermometer{Reading: 0}

See Also:

  • IsoComposeIso: the variant that composes with an Iso[A, B] instead of a Lens[A, B]
  • LensComposeLens: the pure-lens equivalent
  • IsoAsLens: converts an Iso to a Lens directly

func IsoFrom

func IsoFrom[S, A any](a A) func(Iso[S, A]) S

IsoFrom wraps a target value into a source value using an isomorphism. This is an alias for Wrap, provided for semantic clarity when the direction of conversion is important.

Type Parameters:

  • S: The source type to convert from
  • A: The target type

Parameters:

  • a: The target value to convert

Returns:

  • A function that takes an Iso[S, A] and returns the converted value of type S

Example:

type Email string
type ValidatedEmail struct{ value Email }

emailIso := MakeIso(
    func(ve ValidatedEmail) Email { return ve.value },
    func(e Email) ValidatedEmail { return ValidatedEmail{value: e} },
)

// Convert from Email
validated := IsoFrom[ValidatedEmail](Email("admin@example.com"))(emailIso)
// ValidatedEmail{value: "admin@example.com"}

func IsoIMap

func IsoIMap[S, A, B any](ab func(A) B, ba func(B) A) func(Iso[S, A]) Iso[S, B]

IsoIMap bidirectionally maps the target type of an isomorphism. Given Iso[S, A] and functions A → B and B → A, creates Iso[S, B]. This allows you to transform both directions of an isomorphism.

Type Parameters:

  • S: The source type (unchanged)
  • A: The original target type
  • B: The new target type

Parameters:

  • ab: Function to map from A to B
  • ba: Function to map from B to A (inverse of ab)

Returns:

  • A function that transforms Iso[S, A] to Iso[S, B]

Example:

type Celsius float64
type Kelvin float64

celsiusIso := Id[Celsius]()

// Create isomorphism to Kelvin
celsiusToKelvin := F.Pipe1(
    celsiusIso,
    IsoIMap(
        func(c Celsius) Kelvin { return Kelvin(c + 273.15) },
        func(k Kelvin) Celsius { return Celsius(k - 273.15) },
    ),
)

kelvin := celsiusToKelvin.Get(Celsius(20))      // 293.15 K
celsius := celsiusToKelvin.ReverseGet(Kelvin(293.15)) // 20°C

Note: The functions ab and ba must be inverses of each other to maintain the isomorphism laws.

func IsoModify

func IsoModify[S any, FCT ~func(A) A, A any](f FCT) func(Iso[S, A]) Endomorphism[S]

IsoModify creates a function that applies a transformation in the target space. It converts the source value to the target type, applies the transformation, then converts back to the source type.

Type Parameters:

  • S: The source type
  • FCT: The transformation function type (A → A)
  • A: The target type

Parameters:

  • f: The transformation function to apply in the target space

Returns:

  • A function that takes an Iso[S, A] and returns an endomorphism (S → S)

Example:

type Meters float64
type Kilometers float64

mToKm := MakeIso(
    func(m Meters) Kilometers { return Kilometers(m / 1000) },
    func(km Kilometers) Meters { return Meters(km * 1000) },
)

// Double the distance in kilometers, result in meters
doubled := IsoModify[Meters](func(km Kilometers) Kilometers {
    return km * 2
})(mToKm)(Meters(5000))
// Result: Meters(10000)

func IsoTo

func IsoTo[A, S any](s S) func(Iso[S, A]) A

IsoTo extracts the target value from a source value using an isomorphism. This is an alias for Unwrap, provided for semantic clarity when the direction of conversion is important.

Type Parameters:

  • A: The target type to convert to
  • S: The source type

Parameters:

  • s: The source value to convert

Returns:

  • A function that takes an Iso[S, A] and returns the converted value of type A

Example:

type Email string
type ValidatedEmail struct{ value Email }

emailIso := MakeIso(
    func(ve ValidatedEmail) Email { return ve.value },
    func(e Email) ValidatedEmail { return ValidatedEmail{value: e} },
)

// Convert to Email
email := IsoTo[Email](ValidatedEmail{value: "user@example.com"})(emailIso)
// "user@example.com"

func IsoUnwrap

func IsoUnwrap[A, S any](s S) func(Iso[S, A]) A

IsoUnwrap extracts the target value from a source value using an isomorphism. This is a convenience function that applies the Get function of the isomorphism.

Type Parameters:

  • A: The target type to extract
  • S: The source type

Parameters:

  • s: The source value to unwrap

Returns:

  • A function that takes an Iso[S, A] and returns the unwrapped value of type A

Example:

type UserId int

userIdIso := MakeIso(
    func(id UserId) int { return int(id) },
    func(i int) UserId { return UserId(i) },
)

rawId := IsoUnwrap[int](UserId(42))(userIdIso) // 42

Note: This function is also available as To for semantic clarity.

func IsoWrap

func IsoWrap[S, A any](a A) func(Iso[S, A]) S

IsoWrap wraps a target value into a source value using an isomorphism. This is a convenience function that applies the ReverseGet function of the isomorphism.

Type Parameters:

  • S: The source type to wrap into
  • A: The target type

Parameters:

  • a: The target value to wrap

Returns:

  • A function that takes an Iso[S, A] and returns the wrapped value of type S

Example:

type UserId int

userIdIso := MakeIso(
    func(id UserId) int { return int(id) },
    func(i int) UserId { return UserId(i) },
)

userId := IsoWrap[UserId](42)(userIdIso) // UserId(42)

Note: This function is also available as From for semantic clarity.

func LensModify

func LensModify[S any, FCT ~func(A) A, A any](f FCT) func(Lens[S, A]) Endomorphism[S]

LensModify transforms a value through a lens using a transformation F.

Instead of setting a specific value, LensModify applies a function to the current value. This is useful for updates like incrementing a counter, appending to a string, etc. If the transformation doesn't change the value, the original structure is returned.

Type Parameters:

  • S: Structure type
  • FCT: Transformation function type (A → A)
  • A: Focus type

Parameters:

  • f: Transformation function to apply to the focused value

Returns:

  • A function that takes a Lens[S, A] and returns an Endomorphism[S]

Example:

type Counter struct {
    Value int
}

valueLens := lens.MakeLens(
    func(c Counter) int { return c.Value },
    func(c Counter, v int) Counter { c.Value = v; return c },
)

counter := Counter{Value: 5}

// Increment the counter
incremented := F.Pipe2(
    valueLens,
    lens.LensModify[Counter](func(v int) int { return v + 1 }),
    F.Ap(counter),
)
// incremented.Value == 6

// Double the counter
doubled := F.Pipe2(
    valueLens,
    lens.LensModify[Counter](func(v int) int { return v * 2 }),
    F.Ap(counter),
)
// doubled.Value == 10

func LensModifyF

func LensModifyF[S, A, HKTA, HKTS any](
	fmap functor.MapType[A, S, HKTA, HKTS],
) func(func(A) HKTA) func(Lens[S, A]) func(S) HKTS

LensModifyF transforms a value through a lens using a function that returns a value in a functor context.

This is the functorial version of Modify, allowing transformations that produce effects (like Option, Either, IO, etc.) while updating the focused value. The functor's map operation is used to apply the lens's setter to the transformed value, preserving the computational context.

This function corresponds to modifyF from monocle-ts, enabling effectful updates through lenses.

Type Parameters

  • S: Structure type
  • A: Focus type (the value being transformed)
  • HKTA: Higher-kinded type containing the transformed value (e.g., Option[A], Either[E, A])
  • HKTS: Higher-kinded type containing the updated structure (e.g., Option[S], Either[E, S])

Parameters

  • fmap: A functor map operation that transforms A to S within the functor context

Returns

  • A curried function that takes: 1. A transformation function (A → HKTA) 2. A Lens[S, A] 3. A structure S And returns the updated structure in the functor context (HKTS)

Example Usage

type Person struct {
    Name string
    Age  int
}

ageLens := lens.MakeLens(
    func(p Person) int { return p.Age },
    func(p Person, age int) Person { p.Age = age; return p },
)

// Validate age is positive, returning Option
validateAge := func(age int) option.Option[int] {
    if age > 0 {
        return option.Some(age)
    }
    return option.None[int]()
}

// Create a modifier that validates while updating
modifyAge := lens.LensModifyF[Person, int](option.Functor[int, Person]().Map)

person := Person{Name: "Alice", Age: 30}
result := modifyAge(validateAge)(ageLens)(person)
// result is Some(Person{Name: "Alice", Age: 30})

invalidResult := modifyAge(func(age int) option.Option[int] {
    return option.None[int]()
})(ageLens)(person)
// invalidResult is None[Person]()

See Also

  • Modify: Non-functorial version for simple transformations
  • functor.Functor: The functor interface used for mapping

func LensSet

func LensSet[S any, A any](a A) func(Lens[S, A]) Endomorphism[S]

LensSet returns a function that updates the focus of a lens to a constant value.

This is a convenience helper for partially applying a value before supplying the lens, making it useful in composition pipelines with F.Pipe.

Example:

type Counter struct {
    Value int
}

valueLens := lens.MakeLens(
    func(c Counter) int { return c.Value },
    func(c Counter, value int) Counter {
        c.Value = value
        return c
    },
)

counter := Counter{Value: 5}
updated := F.Pipe2(
    10,
    lens.LensSet[Counter](10),
    F.Ap(valueLens),
)
// updated.Value == 10

func OptionalAsTraversal

func OptionalAsTraversal[R ~func(func(A) HKTA) func(S) HKTS, S, A, HKTS, HKTA any](
	fof pointed.OfType[S, HKTS],
	fmap functor.MapType[A, S, HKTA, HKTS],
) func(Optional[S, A]) R

func OptionalFromPredicate

func OptionalFromPredicate[S, A any](pred func(A) bool) func(func(S) A, func(S, A) S) Optional[S, A]

OptionalFromPredicate creates an optional from getter and setter functions. It checks for optional values and the correct update procedure

func OptionalFromPredicateRef

func OptionalFromPredicateRef[S, A any](pred func(A) bool) func(func(*S) A, func(*S, A) *S) Optional[*S, A]

FromPredicate creates an optional from getter and setter functions. It checks for optional values and the correct update procedure

func OptionalModifyOption

func OptionalModifyOption[S, A any](f func(A) A) func(Optional[S, A]) O.Kleisli[S, S]

func OptionalSetOption

func OptionalSetOption[S, A any](a A) func(Optional[S, A]) O.Kleisli[S, S]

func PrismAsTraversal

func PrismAsTraversal[R ~func(func(A) HKTA) func(S) HKTS, S, A, HKTS, HKTA any](
	fof pointed.OfType[S, HKTS],
	fmap functor.MapType[A, S, HKTA, HKTS],
) func(Prism[S, A]) R

PrismAsTraversal converts a Prism into a Traversal.

A Traversal is a more general optic that can focus on zero or more values, while a Prism focuses on zero or one value. This function lifts a Prism into the Traversal abstraction, allowing it to be used in contexts that expect traversals.

The conversion works by:

  • If the prism matches (GetOption returns Some), the traversal focuses on that value
  • If the prism doesn't match (GetOption returns None), the traversal focuses on zero values

Type Parameters:

  • R: The traversal function type ~func(func(A) HKTA) func(S) HKTS
  • S: The source type
  • A: The focus type
  • HKTS: Higher-kinded type for S (e.g., functor/applicative context)
  • HKTA: Higher-kinded type for A (e.g., functor/applicative context)

Parameters:

  • fof: Function to lift S into the higher-kinded type HKTS (pure/of operation)
  • fmap: Function to map over HKTA and produce HKTS (functor map operation)

Returns:

  • A function that converts a Prism[S, A] into a Traversal R

Example:

// Convert a prism to a traversal for use with applicative functors
prism := MakePrism(...)
traversal := AsTraversal(
    func(s S) HKTS { return pure(s) },
    func(hkta HKTA, f func(A) S) HKTS { return fmap(hkta, f) },
)(prism)

Note: This function is typically used in advanced scenarios involving higher-kinded types and applicative functors. Most users will work directly with prisms rather than converting them to traversals.

func PrismSet

func PrismSet[S, A any](a A) func(Prism[S, A]) Endomorphism[S]

PrismSet creates a function that sets a value through a prism. If the prism matches, it replaces the focused value with the new value. If the prism doesn't match, it returns the original value unchanged.

Parameters:

  • a: The new value to set

Returns:

  • A function that takes a prism and returns an endomorphism (S → S)

Example:

somePrism := MakePrism(...)
setter := PrismSet[Option[int], int](100)
result := setter(somePrism)(Some(42))  // Some(100)
result = setter(somePrism)(None[int]()) // None[int]() (unchanged)

Types

type Endomorphism

type Endomorphism[A any] = endomorphism.Endomorphism[A]

Endomorphism is a function from a type to itself (A → A). It represents transformations that preserve the type.

type Iso

type Iso[S, A any] struct {

	// Get converts a value from the source type S to the target type A.
	Get func(s S) A

	// ReverseGet converts a value from the target type A back to the source type S.
	// This is the inverse of Get.
	ReverseGet func(a A) S
	// contains filtered or unexported fields
}

Iso represents an isomorphism between types S and A. An isomorphism is a bidirectional transformation that converts between two types without any loss of information. It consists of two functions that are inverses of each other.

Type Parameters:

  • S: The source type
  • A: The target type

Fields:

  • Get: Converts from S to A
  • ReverseGet: Converts from A back to S

Laws: An Iso must satisfy the round-trip laws:

  1. ReverseGet(Get(s)) == s for all s: S
  2. Get(ReverseGet(a)) == a for all a: A

Example:

// Isomorphism between Celsius and Fahrenheit
tempIso := Iso[float64, float64]{
    Get: func(c float64) float64 { return c*9/5 + 32 },
    ReverseGet: func(f float64) float64 { return (f - 32) * 5 / 9 },
}

fahrenheit := tempIso.Get(20.0)        // 68.0
celsius := tempIso.ReverseGet(68.0)    // 20.0

func IsoId

func IsoId[S any]() Iso[S, S]

IsoId returns an identity isomorphism that performs no transformation. Both Get and ReverseGet are the identity function.

Type Parameters:

  • S: The type for both source and target

Returns:

  • An Iso[S, S] where Get and ReverseGet are both identity functions

Example:

idIso := IsoId[int]()
value := idIso.Get(42)        // 42
same := idIso.ReverseGet(42)  // 42

Use cases:

  • As a starting point for isomorphism composition
  • When you need an isomorphism but don't want to transform the value
  • In generic code that requires an isomorphism parameter

func IsoReverse

func IsoReverse[S, A any](sa Iso[S, A]) Iso[A, S]

IsoReverse swaps the direction of an isomorphism. Given Iso[S, A], creates Iso[A, S] where Get and ReverseGet are swapped.

Type Parameters:

  • S: The original source type (becomes target)
  • A: The original target type (becomes source)

Parameters:

  • sa: The isomorphism to reverse

Returns:

  • An Iso[A, S] with Get and ReverseGet swapped

Example:

celsiusToFahrenheit := MakeIso(
    func(c float64) float64 { return c*9/5 + 32 },
    func(f float64) float64 { return (f - 32) * 5 / 9 },
)

// IsoReverse to get Fahrenheit to Celsius
fahrenheitToCelsius := IsoReverse(celsiusToFahrenheit)

celsius := fahrenheitToCelsius.Get(68.0)        // 20.0
fahrenheit := fahrenheitToCelsius.ReverseGet(20.0) // 68.0

func MakeIso

func MakeIso[S, A any](get func(S) A, reverse func(A) S) Iso[S, A]

MakeIso constructs an isomorphism from two functions. The functions should be inverses of each other to satisfy the isomorphism laws.

Type Parameters:

  • S: The source type
  • A: The target type

Parameters:

  • get: Function to convert from S to A
  • reverse: Function to convert from A to S (inverse of get)

Returns:

  • An Iso[S, A] that uses the provided functions

Example:

// Create an isomorphism between string and []byte
stringBytesIso := MakeIso(
    func(s string) []byte { return []byte(s) },
    func(b []byte) string { return string(b) },
)

bytes := stringBytesIso.Get("hello")           // []byte("hello")
str := stringBytesIso.ReverseGet([]byte("hi")) // "hi"

func (Iso[S, A]) Compose

func (p Iso[S, A]) Compose[B any](ab Iso[A, B]) Iso[S, B]

Compose returns a new isomorphism that focuses deeper by chaining this isomorphism (S → A) with an inner isomorphism (A → B), producing a composed isomorphism (S → B).

This is the method-receiver form of the package-level OptionalComposeOptional function, available only on Go 1.27 and later because Go did not support type parameters on methods before that version. On earlier toolchains, use the equivalent free function instead:

// method form (go1.27+)
composed := outerIso.Compose(innerIso)

// free-function form (all versions)
composed := iso.Compose[S](innerIso)(outerIso)

Get of the composed isomorphism applies the outer Get followed by the inner Get: composed.Get(s) = ab.Get(sa.Get(s)).

ReverseGet of the composed isomorphism applies the inner ReverseGet followed by the outer ReverseGet: composed.ReverseGet(b) = sa.ReverseGet(ab.ReverseGet(b)).

The composed isomorphism satisfies both iso round-trip laws whenever both constituent isomorphisms individually satisfy them:

composed.ReverseGet(composed.Get(s)) == s    for all s: S
composed.Get(composed.ReverseGet(b)) == b    for all b: B

Type Parameters:

  • B: the target type of the inner isomorphism and of the resulting isomorphism

Parameters:

  • ab: the inner isomorphism from A to B

Returns:

  • Iso[S, B]: a new isomorphism from S directly to B

See Also:

  • Compose: the equivalent package-level function
  • Reverse: swaps the direction of an isomorphism

func (Iso[S, A]) ComposeLens

func (p Iso[S, A]) ComposeLens[B any](ab Lens[A, B]) Lens[S, B]

ComposeLens returns a new lens by composing this isomorphism (S ↔ A) with an inner lens (A → B), producing a Lens[S, B].

Internally the isomorphism is first converted to a Lens[S, A] via IsoAsLens, then composed with the provided lens using LensComposeLens. The result focuses on a value of type B that is reached by first applying the isomorphism's Get direction and then the inner lens's Get.

This is the method-receiver form of the package-level IsoComposeLens function, available only on Go 1.27 and later because Go did not support type parameters on methods before that version. On earlier toolchains use the equivalent free function:

// method form (go1.27+)
streetLens := addressIso.ComposeLens(streetFieldLens)

// free-function form (all versions)
streetLens := IsoComposeLens[S](streetFieldLens)(addressIso)

The resulting lens satisfies all three lens laws whenever both the isomorphism and the inner lens individually satisfy them.

Type Parameters:

  • B: the focus type of the inner lens and of the resulting lens

Parameters:

  • ab: the inner lens from A to B

Returns:

  • Lens[S, B]: a new lens from S directly to B

See Also:

  • Compose: the equivalent method for composing with an Iso[A, B] instead of a Lens[A, B]
  • IsoComposeLens: the equivalent package-level function

func (Iso) Format

func (Iso) Format(f fmt.State, c rune)

func (Iso) LogValue

func (Iso) LogValue() slog.Value

func (Iso) String

func (Iso) String() string

String returns a string representation of the isomorphism.

Example:

tempIso := iso.MakeIso(...)
fmt.Println(tempIso)  // Prints: "Iso"

type Lens

type Lens[S, A any] struct {

	// Get extracts the focused value of type A from structure S.
	Get func(s S) A

	// Set returns a function that updates the focused value in structure S.
	// The returned function takes a structure S and returns a new structure S
	// with the focused value updated to a. The original structure is never modified.
	Set func(a A) Endomorphism[S]
	// contains filtered or unexported fields
}

Lens is a functional reference to a subpart of a data structure.

A Lens[S, A] provides a composable way to focus on a field of type A within a structure of type S. It consists of two operations:

  • Get: Extracts the focused value from the structure (S → A)
  • Set: Updates the focused value in the structure, returning a new structure (A → S → S)

Lenses maintain immutability by always returning new copies of the structure when setting values, never modifying the original.

Type Parameters:

  • S: The source/structure type (the whole)
  • A: The focus/field type (the part)

Lens Laws:

A well-behaved lens must satisfy three laws:

  1. GetSet (You get what you set): lens.Set(lens.Get(s))(s) == s
  1. SetGet (You set what you get): lens.Get(lens.Set(a)(s)) == a
  1. SetSet (Setting twice is the same as setting once): lens.Set(a2)(lens.Set(a1)(s)) == lens.Set(a2)(s)

Example Usage:

type Person struct {
    Name string
    Age  int
}

// Create a lens focusing on the Name field
nameLens := lens.MakeLens(
    func(p Person) string { return p.Name },
    func(name string) func(Person) Person {
        return func(p Person) Person {
            return Person{Name: name, Age: p.Age}
        }
    },
)

person := Person{Name: "Alice", Age: 30}
name := nameLens.Get(person)           // Returns: "Alice"
updated := nameLens.Set("Bob")(person) // Returns: Person{Name: "Bob", Age: 30}
// Original person remains unchanged (immutability preserved)

func IsoAsLens

func IsoAsLens[S, A any](sa Iso[S, A]) Lens[S, A]

IsoAsLens converts an Iso[S, A] into a Lens[S, A].

The resulting lens uses the iso's Get as its getter and its ReverseGet as its setter. Because an isomorphism is total and invertible, Set always replaces the focused value by applying ReverseGet — the original S is discarded.

Type Parameters:

  • S: The source type of the isomorphism and of the resulting lens
  • A: The target type of the isomorphism and the focus type of the resulting lens

Parameters:

  • sa: The isomorphism to convert

Returns:

  • Lens[S, A]: a lens whose Get is sa.Get and whose Set is sa.ReverseGet

See Also:

  • IsoAsLensRef: the pointer-receiver variant for Iso[*S, A]

func IsoAsLensRef deprecated

func IsoAsLensRef[S, A any](sa Iso[*S, A]) Lens[*S, A]

IsoAsLensRef converts an Iso[*S, A] into a Lens[*S, A] for pointer-based structures.

This is the pointer-receiver variant of IsoAsLens. It accepts an isomorphism whose source type is already a pointer (*S) and wraps the resulting lens with copy-on-write semantics: before the setter is applied, a shallow copy of *S is made so that the original value is never mutated. A nil pointer is handled safely by substituting a zero-value S.

In practice the copy-on-write wrapper is a no-op for an iso-derived setter, because the setter is simply ReverseGet(a) and ignores the incoming *S entirely. The wrapper exists for consistency with other pointer lenses and to guard against nil inputs.

Deprecated: IsoAsLens[*S, A] is sufficient because the copy-on-write wrapper added by this function has no observable effect for iso-derived setters. Prefer IsoAsLens unless you specifically need the nil-pointer safety on the incoming *S, which the iso's ReverseGet already cannot depend on.

Type Parameters:

  • S: The pointee type; the isomorphism source is *S
  • A: The target type of the isomorphism and the focus type of the resulting lens

Parameters:

  • sa: The isomorphism from *S to A to convert

Returns:

  • Lens[*S, A]: a lens whose Get is sa.Get and whose Set applies sa.ReverseGet with copy-on-write protection on the *S receiver

See Also:

  • IsoAsLens: the general form that accepts any Iso[S, A], including Iso[*S, A]

func LensId

func LensId[S any]() Lens[S, S]

LensId returns an identity Lens that focuses on the entire structure.

The identity lens is useful as a starting point for lens composition or when you need a lens that doesn't actually focus on a subpart. Get returns the structure unchanged, and Set replaces the entire structure.

Type Parameters:

  • S: The structure type

Returns:

  • A Lens[S, S] where both source and focus are the same type

Example:

type Person struct {
    Name string
    Age  int
}

idLens := lens.LensId[Person]()
person := Person{Name: "Alice", Age: 30}

same := idLens.Get(person)  // Returns person unchanged
replaced := idLens.Set(Person{Name: "Bob", Age: 25})(person)
// replaced is Person{Name: "Bob", Age: 25}

func LensIdRef

func LensIdRef[S any]() Lens[*S, *S]

LensIdRef returns an identity Lens for pointer-based structures.

This is the pointer version of LensId. It focuses on the entire pointer structure, with automatic copying to ensure immutability.

Type Parameters:

  • S: The structure type (will be used as *S)

Returns:

  • A Lens[*S, *S] where both source and focus are pointers to the same type

Example:

idLens := lens.LensIdRef[Person]()
person := &Person{Name: "Alice", Age: 30}

same := idLens.Get(person)  // Returns person pointer
replaced := idLens.Set(&Person{Name: "Bob", Age: 25})(person)
// person.Name is still "Alice", replaced is a new pointer

func MakeLens

func MakeLens[GET ~func(S) A, SET ~func(S, A) S, S, A any](get GET, set SET) Lens[S, A]

MakeLens creates a Lens based on a getter and a setter F.

The setter must create a (shallow) copy of the data structure. This happens automatically when the data is passed by value. For pointer-based structures, use MakeLensRef instead. For other reference types (slices, maps), ensure the setter creates a copy.

Type Parameters:

  • GET: Getter function type (S → A)
  • SET: Setter function type (S, A → S)
  • S: Source structure type
  • A: Focus/field type

Parameters:

  • get: Function to extract value A from structure S
  • set: Function to update value A in structure S, returning a new S

Returns:

  • A Lens[S, A] that can get and set values immutably

Example:

type Person struct {
    Name string
    Age  int
}

nameLens := lens.MakeLens(
    func(p Person) string { return p.Name },
    func(p Person, name string) Person {
        p.Name = name
        return p
    },
)

person := Person{Name: "Alice", Age: 30}
name := nameLens.Get(person)           // "Alice"
updated := nameLens.Set("Bob")(person) // Person{Name: "Bob", Age: 30}

func MakeLensCurried

func MakeLensCurried[GET ~func(S) A, SET ~func(A) Endomorphism[S], S, A any](get GET, set SET) Lens[S, A]

MakeLensCurried creates a Lens with a curried setter F.

This is similar to MakeLens but accepts a curried setter (A → S → S) instead of an uncurried one (S, A → S). The curried form is more composable in functional pipelines.

The setter must create a (shallow) copy of the data structure. This happens automatically when the data is passed by value. For pointer-based structures, use MakeLensRefCurried.

Type Parameters:

  • GET: Getter function type (S → A)
  • SET: Curried setter function type (A → S → S)
  • S: Source structure type
  • A: Focus/field type

Parameters:

  • get: Function to extract value A from structure S
  • set: Curried function to update value A in structure S

Returns:

  • A Lens[S, A] that can get and set values immutably

Example:

nameLens := lens.MakeLensCurried(
    func(p Person) string { return p.Name },
    func(name string) func(Person) Person {
        return func(p Person) Person {
            p.Name = name
            return p
        }
    },
)

func MakeLensCurriedRefWithName

func MakeLensCurriedRefWithName[GET ~func(*S) A, SET ~func(A) Endomorphism[*S], S, A any](get GET, set SET, name string) Lens[*S, A]

MakeLensCurriedRefWithName creates a Lens for pointer-based structures with a curried setter and a custom name.

This is the named variant of MakeLensCurriedRefWithName combined with automatic copy-on-write semantics for pointer receivers. The setter does not need to create a copy manually; the copy is applied by the wrapped curried setter produced by setCopyCurried. The name is used in String, Format, and LogValue for debugging and structured logging.

Type Parameters:

  • GET: Getter function type (*S → A)
  • SET: Curried setter function type (A → *S → *S)
  • S: Source structure type (will be used as *S)
  • A: Focus/field type

Parameters:

  • get: Function to extract value A from pointer *S
  • set: Curried function to update value A in pointer *S (copying handled automatically)
  • name: A descriptive name for the lens (used in String() and Format())

Returns:

  • A Lens[*S, A] with the specified name

func MakeLensCurriedWithName

func MakeLensCurriedWithName[GET ~func(S) A, SET ~func(A) Endomorphism[S], S, A any](get GET, set SET, name string) Lens[S, A]

MakeLensCurriedWithName creates a Lens with a curried setter and a custom name.

This combines the benefits of MakeLensCurried (curried setter for better composition) with MakeLensWithName (custom name for debugging). The name is useful for debugging complex lens compositions and understanding which lens is being used in error messages or logs.

The setter must create a (shallow) copy of the data structure. This happens automatically when the data is passed by value. For pointer-based structures, use MakeLensRefCurried.

Type Parameters:

  • GET: Getter function type (S → A)
  • SET: Curried setter function type (A → S → S)
  • S: Source structure type
  • A: Focus/field type

Parameters:

  • get: Function to extract value A from structure S
  • set: Curried function to update value A in structure S
  • name: A descriptive name for the lens (used in String() and Format())

Returns:

  • A Lens[S, A] with the specified name

Example:

type Person struct {
    Name string
    Age  int
}

nameLens := lens.MakeLensCurriedWithName(
    func(p Person) string { return p.Name },
    func(name string) func(Person) Person {
        return func(p Person) Person {
            p.Name = name
            return p
        }
    },
    "Person.Name",
)

fmt.Printf("Using lens: %s\n", nameLens)  // Prints: "Using lens: Person.Name"

func MakeLensRef

func MakeLensRef[GET ~func(*S) A, SET func(*S, A) *S, S, A any](get GET, set SET) Lens[*S, A]

MakeLensRef creates a Lens for pointer-based structures.

Unlike MakeLens, the setter does not need to create a copy manually. This function automatically wraps the setter to create a shallow copy of the pointed-to value before modification, ensuring immutability.

This lens assumes that property A always exists in structure S (i.e., it's not optional).

Type Parameters:

  • GET: Getter function type (*S → A)
  • SET: Setter function type (*S, A → *S)
  • S: Source structure type (will be used as *S)
  • A: Focus/field type

Parameters:

  • get: Function to extract value A from pointer *S
  • set: Function to update value A in pointer *S (copying handled automatically)

Returns:

  • A Lens[*S, A] that can get and set values immutably on pointers

Example:

type Person struct {
    Name string
    Age  int
}

nameLens := lens.MakeLensRef(
    func(p *Person) string { return p.Name },
    func(p *Person, name string) *Person {
        p.Name = name  // No manual copy needed
        return p
    },
)

person := &Person{Name: "Alice", Age: 30}
updated := nameLens.Set("Bob")(person)
// person.Name is still "Alice", updated is a new pointer with Name "Bob"

func MakeLensRefCurried

func MakeLensRefCurried[S, A any](get func(*S) A, set func(A) Endomorphism[*S]) Lens[*S, A]

MakeLensRefCurried creates a Lens for pointer-based structures with a curried setter.

This combines the benefits of MakeLensRef (automatic copying) with MakeLensCurried (curried setter for better composition). The setter does not need to create a copy manually; this function automatically wraps it to ensure immutability.

This lens assumes that property A always exists in structure S (i.e., it's not optional).

Type Parameters:

  • S: Source structure type (will be used as *S)
  • A: Focus/field type

Parameters:

  • get: Function to extract value A from pointer *S
  • set: Curried function to update value A in pointer *S (copying handled automatically)

Returns:

  • A Lens[*S, A] that can get and set values immutably on pointers

Example:

nameLens := lens.MakeLensRefCurried(
    func(p *Person) string { return p.Name },
    func(name string) func(*Person) *Person {
        return func(p *Person) *Person {
            p.Name = name  // No manual copy needed
            return p
        }
    },
)

func MakeLensRefCurriedWithName

func MakeLensRefCurriedWithName[S, A any](get func(*S) A, set func(A) Endomorphism[*S], name string) Lens[*S, A]

MakeLensRefCurriedWithName creates a Lens for pointer-based structures with a curried setter and custom name.

This combines the benefits of MakeLensRefCurried (automatic copying with curried setter) with MakeLensWithName (custom name for debugging). The setter does not need to create a copy manually; this function automatically wraps it to ensure immutability. The curried form is more composable in functional pipelines, and the name is useful for debugging.

This lens assumes that property A always exists in structure S (i.e., it's not optional).

Type Parameters:

  • S: Source structure type (will be used as *S)
  • A: Focus/field type

Parameters:

  • get: Function to extract value A from pointer *S
  • set: Curried function to update value A in pointer *S (copying handled automatically)
  • name: A descriptive name for the lens (used in String() and Format())

Returns:

  • A Lens[*S, A] with the specified name

Example:

type Person struct {
    Name string
    Age  int
}

nameLens := lens.MakeLensRefCurriedWithName(
    func(p *Person) string { return p.Name },
    func(name string) func(*Person) *Person {
        return func(p *Person) *Person {
            p.Name = name  // No manual copy needed
            return p
        }
    },
    "Person.Name",
)

person := &Person{Name: "Alice", Age: 30}
fmt.Printf("Using lens: %s\n", nameLens)  // Prints: "Using lens: Person.Name"
updated := nameLens.Set("Bob")(person)
// person.Name is still "Alice", updated is a new pointer with Name "Bob"

func MakeLensRefWithName

func MakeLensRefWithName[GET ~func(*S) A, SET func(*S, A) *S, S, A any](get GET, set SET, name string) Lens[*S, A]

MakeLensRefWithName creates a Lens for pointer-based structures with a custom name.

This combines MakeLensRef (automatic copying for pointer structures) with MakeLensWithName (custom name for debugging). The setter does not need to create a copy manually; this function automatically wraps it to ensure immutability. The name is useful for debugging complex lens compositions and understanding which lens is being used in error messages or logs.

This lens assumes that property A always exists in structure S (i.e., it's not optional).

Type Parameters:

  • GET: Getter function type (*S → A)
  • SET: Setter function type (*S, A → *S)
  • S: Source structure type (will be used as *S)
  • A: Focus/field type

Parameters:

  • get: Function to extract value A from pointer *S
  • set: Function to update value A in pointer *S (copying handled automatically)
  • name: A descriptive name for the lens (used in String() and Format())

Returns:

  • A Lens[*S, A] with the specified name

Example:

type Person struct {
    Name string
    Age  int
}

nameLens := lens.MakeLensRefWithName(
    func(p *Person) string { return p.Name },
    func(p *Person, name string) *Person {
        p.Name = name  // No manual copy needed
        return p
    },
    "Person.Name",
)

person := &Person{Name: "Alice", Age: 30}
fmt.Printf("Using lens: %s\n", nameLens)  // Prints: "Using lens: Person.Name"
updated := nameLens.Set("Bob")(person)
// person.Name is still "Alice", updated is a new pointer with Name "Bob"

func MakeLensStrict

func MakeLensStrict[GET ~func(*S) A, SET func(*S, A) *S, S any, A comparable](get GET, set SET) Lens[*S, A]

MakeLensStrict creates a Lens for pointer-based structures with strict equality optimization.

This is a convenience function that combines MakeLensWithEq with strict equality comparison (==). It's suitable for comparable types (primitives, strings, pointers, etc.) and provides the same optimization as MakeLensWithEq: if the new value equals the current value, the original pointer is returned unchanged instead of creating a copy.

The setter does not need to create a copy manually; this function automatically wraps it to ensure immutability when changes are made.

This lens assumes that property A always exists in structure S (i.e., it's not optional).

Type Parameters:

  • GET: Getter function type (*S → A)
  • SET: Setter function type (*S, A → *S)
  • S: Source structure type (will be used as *S)
  • A: Focus/field type (must be comparable)

Parameters:

  • get: Function to extract value A from pointer *S
  • set: Function to update value A in pointer *S (copying handled automatically)

Returns:

  • A Lens[*S, A] that can get and set values immutably on pointers with strict equality optimization

Example:

type Person struct {
    Name string
    Age  int
}

// Using MakeLensStrict for a string field (comparable type)
nameLens := lens.MakeLensStrict(
    func(p *Person) string { return p.Name },
    func(p *Person, name string) *Person {
        p.Name = name  // No manual copy needed
        return p
    },
)

person := &Person{Name: "Alice", Age: 30}

// Setting the same value returns the original pointer (no copy)
same := nameLens.Set("Alice")(person)
// same == person (same pointer)

// Setting a different value creates a new copy
updated := nameLens.Set("Bob")(person)
// person.Name is still "Alice", updated is a new pointer with Name "Bob"

func MakeLensStrictWithName

func MakeLensStrictWithName[GET ~func(*S) A, SET func(*S, A) *S, S any, A comparable](get GET, set SET, name string) Lens[*S, A]

MakeLensStrictWithName creates a Lens for pointer-based structures with strict equality optimization and a custom name.

This combines MakeLensStrict (strict equality optimization using ==) with MakeLensWithName (custom name for debugging). It's a convenience function suitable for comparable types (primitives, strings, pointers, etc.). If the new value equals the current value, the original pointer is returned unchanged instead of creating a copy. The name is useful for debugging.

The setter does not need to create a copy manually; this function automatically wraps it to ensure immutability when changes are made.

This lens assumes that property A always exists in structure S (i.e., it's not optional).

Type Parameters:

  • GET: Getter function type (*S → A)
  • SET: Setter function type (*S, A → *S)
  • S: Source structure type (will be used as *S)
  • A: Focus/field type (must be comparable)

Parameters:

  • get: Function to extract value A from pointer *S
  • set: Function to update value A in pointer *S (copying handled automatically)
  • name: A descriptive name for the lens (used in String() and Format())

Returns:

  • A Lens[*S, A] with strict equality optimization and the specified name

Example:

type Person struct {
    Name string
    Age  int
}

// Using MakeLensStrictWithName for a string field (comparable type)
nameLens := lens.MakeLensStrictWithName(
    func(p *Person) string { return p.Name },
    func(p *Person, name string) *Person {
        p.Name = name  // No manual copy needed
        return p
    },
    "Person.Name",
)

person := &Person{Name: "Alice", Age: 30}
fmt.Printf("Using lens: %s\n", nameLens)  // Prints: "Using lens: Person.Name"

// Setting the same value returns the original pointer (no copy)
same := nameLens.Set("Alice")(person)  // same == person

// Setting a different value creates a new copy
updated := nameLens.Set("Bob")(person)  // person.Name still "Alice"

func MakeLensWithEq

func MakeLensWithEq[GET ~func(*S) A, SET func(*S, A) *S, S, A any](pred EQ.Eq[A], get GET, set SET) Lens[*S, A]

MakeLensWithEq creates a Lens for pointer-based structures with equality optimization.

This is similar to MakeLensRef but includes an optimization: if the new value equals the current value (according to the provided Eq predicate), the original pointer is returned unchanged instead of creating a copy. This can improve performance and reduce allocations when setting values that don't actually change the structure.

The setter does not need to create a copy manually; this function automatically wraps it to ensure immutability when changes are made.

This lens assumes that property A always exists in structure S (i.e., it's not optional).

Type Parameters:

  • GET: Getter function type (*S → A)
  • SET: Setter function type (*S, A → *S)
  • S: Source structure type (will be used as *S)
  • A: Focus/field type

Parameters:

  • pred: Equality predicate to compare values of type A
  • get: Function to extract value A from pointer *S
  • set: Function to update value A in pointer *S (copying handled automatically)

Returns:

  • A Lens[*S, A] that can get and set values immutably on pointers with equality optimization

Example:

type Person struct {
    Name string
    Age  int
}

nameLens := lens.MakeLensWithEq(
    eq.FromStrictEquals[string](),
    func(p *Person) string { return p.Name },
    func(p *Person, name string) *Person {
        p.Name = name  // No manual copy needed
        return p
    },
)

person := &Person{Name: "Alice", Age: 30}

// Setting the same value returns the original pointer (no copy)
same := nameLens.Set("Alice")(person)
// same == person (same pointer)

// Setting a different value creates a new copy
updated := nameLens.Set("Bob")(person)
// person.Name is still "Alice", updated is a new pointer with Name "Bob"

func MakeLensWithEqWithName

func MakeLensWithEqWithName[GET ~func(*S) A, SET func(*S, A) *S, S, A any](pred EQ.Eq[A], get GET, set SET, name string) Lens[*S, A]

MakeLensWithEqWithName creates a Lens for pointer-based structures with equality optimization and a custom name.

This combines MakeLensWithEq (equality optimization) with MakeLensWithName (custom name for debugging). If the new value equals the current value (according to the provided Eq predicate), the original pointer is returned unchanged instead of creating a copy. The name is useful for debugging complex lens compositions.

The setter does not need to create a copy manually; this function automatically wraps it to ensure immutability when changes are made.

This lens assumes that property A always exists in structure S (i.e., it's not optional).

Type Parameters:

  • GET: Getter function type (*S → A)
  • SET: Setter function type (*S, A → *S)
  • S: Source structure type (will be used as *S)
  • A: Focus/field type

Parameters:

  • pred: Equality predicate to compare values of type A
  • get: Function to extract value A from pointer *S
  • set: Function to update value A in pointer *S (copying handled automatically)
  • name: A descriptive name for the lens (used in String() and Format())

Returns:

  • A Lens[*S, A] with equality optimization and the specified name

Example:

type Person struct {
    Name string
    Age  int
}

nameLens := lens.MakeLensWithEqWithName(
    eq.FromStrictEquals[string](),
    func(p *Person) string { return p.Name },
    func(p *Person, name string) *Person {
        p.Name = name  // No manual copy needed
        return p
    },
    "Person.Name",
)

person := &Person{Name: "Alice", Age: 30}
fmt.Printf("Using lens: %s\n", nameLens)  // Prints: "Using lens: Person.Name"

// Setting the same value returns the original pointer (no copy)
same := nameLens.Set("Alice")(person)  // same == person

// Setting a different value creates a new copy
updated := nameLens.Set("Bob")(person)  // person.Name still "Alice"

func MakeLensWithName

func MakeLensWithName[GET ~func(S) A, SET ~func(S, A) S, S, A any](get GET, set SET, name string) Lens[S, A]

MakeLensWithName creates a Lens with a custom name for debugging and logging.

This is identical to MakeLens but allows you to specify a name that will be used when the lens is printed or formatted. The name is useful for debugging complex lens compositions and understanding which lens is being used in error messages or logs.

The setter must create a (shallow) copy of the data structure. This happens automatically when the data is passed by value. For pointer-based structures, use MakeLensRef instead.

Type Parameters:

  • GET: Getter function type (S → A)
  • SET: Setter function type (S, A → S)
  • S: Source structure type
  • A: Focus/field type

Parameters:

  • get: Function to extract value A from structure S
  • set: Function to update value A in structure S, returning a new S
  • name: A descriptive name for the lens (used in String() and Format())

Returns:

  • A Lens[S, A] with the specified name

Example:

type Person struct {
    Name string
    Age  int
}

nameLens := lens.MakeLensWithName(
    func(p Person) string { return p.Name },
    func(p Person, name string) Person {
        p.Name = name
        return p
    },
    "Person.Name",
)

fmt.Printf("Using lens: %s\n", nameLens)  // Prints: "Using lens: Person.Name"

func (Lens[S, A]) Compose

func (l Lens[S, A]) Compose[B any](ab Lens[A, B]) Lens[S, B]

Compose returns a new lens that focuses deeper into the structure by chaining this lens (S → A) with an inner lens (A → B), producing a composed lens (S → B).

This is the method-receiver form of the package-level IsoComposeIso function, available only on Go 1.27 and later because Go did not support type parameters on methods before that version. On earlier toolchains, use the equivalent free function instead:

// method form (go1.27+)
personStreetLens := addressLens.Compose(streetLens)

// free-function form (all versions)
personStreetLens := lens.Compose[Person](streetLens)(addressLens)

The composed lens satisfies all three lens laws whenever both constituent lenses individually satisfy them.

Type Parameters:

  • B: the focus type of the inner lens and of the resulting lens

Parameters:

  • ab: the inner lens from A to B

Returns:

  • Lens[S, B]: a new lens from S directly to B

See Also:

  • Compose: the equivalent package-level function

func (Lens[S, A]) ComposeIso

func (l Lens[S, A]) ComposeIso[B any](ab Iso[A, B]) Lens[S, B]

ComposeIso returns a new lens that focuses on a value of type B by composing this lens (S → A) with an isomorphism (A ↔ B), producing a lens (S → B).

This is the method-receiver form of the package-level LensComposeIso function, available only on Go 1.27 and later because Go did not support type parameters on methods before that version. On earlier toolchains use the equivalent free function:

// method form (go1.27+)
fahrenheitLens := readingLens.ComposeIso(celsiusToFahrenheit)

// free-function form (all versions)
fahrenheitLens := LensComposeIso[Thermometer](celsiusToFahrenheit)(readingLens)

The resulting lens satisfies all three lens laws whenever both the outer lens and the isomorphism individually satisfy them.

Type Parameters:

  • B: the focus type of the resulting lens — the target type of the isomorphism

Parameters:

  • ab: the isomorphism from A to B

Returns:

  • Lens[S, B]: a new lens from S directly to B

See Also:

  • Compose: the equivalent method for composing with a Lens[A, B] instead of an Iso[A, B]
  • LensComposeIso: the equivalent package-level function

func (Lens) Format

func (l Lens) Format(f fmt.State, c rune)

Format implements fmt.Formatter.

Supports all standard format verbs:

  • %s, %v, %+v, %q, and all other verbs: uses the String() representation (the lens name)

func (Lens) LogValue

func (l Lens) LogValue() slog.Value

LogValue implements slog.LogValuer.

Returns a slog.Value that represents the lens for structured logging. The lens name is logged as a string value.

func (Lens[S, A]) Modify

func (la Lens[S, A]) Modify(f Endomorphism[A]) Endomorphism[S]

Modify applies a transformation function to the value focused by the lens.

This method transforms the focused value within a structure by applying an endomorphism (a function from A to A) and returns a new structure with the transformed value. The transformation is applied by first getting the current value, applying the function, and then setting the result back into the structure.

Type Parameters:

  • S: The structure type containing the focused value
  • A: The type of the focused value

Parameters:

  • f: An endomorphism that transforms the focused value

Returns:

  • An endomorphism on S that applies the transformation

Example:

type Person struct {
    Name string
    Age  int
}

ageLens := lens.MakeLens(
    func(p Person) int { return p.Age },
    func(p Person, age int) Person { p.Age = age; return p },
)

person := Person{Name: "Alice", Age: 30}
incrementAge := ageLens.Modify(func(age int) int { return age + 1 })
older := incrementAge(person)
// older.Age == 31, person.Age == 30 (original unchanged)

See Also:

  • Modify: Package-level function for use in pipelines
  • Set: For setting a constant value instead of transforming

func (Lens) String

func (l Lens) String() string

String returns the name of the lens for debugging and display purposes.

type LensKleisli

type LensKleisli[S, A, B any] = func(A) Lens[S, B]

LensKleisli represents a function that takes a value of type A and returns a Lens[S, B]. This is useful for composing lenses in a monadic style, allowing for dynamic lens creation based on input values.

Type Parameters:

  • S: The source/structure type
  • A: The input type
  • B: The focus type of the resulting lens

type LensOperator

type LensOperator[S, A, B any] = LensKleisli[S, Lens[S, A], B]

LensOperator is a specialized Kleisli that takes a Lens[S, A] and returns a Lens[S, B]. This enables lens transformations and compositions where one lens is used to derive another.

Type Parameters:

  • S: The source/structure type
  • A: The focus type of the input lens
  • B: The focus type of the resulting lens

func LensComposeIso

func LensComposeIso[S, A, B any](ab Iso[A, B]) LensOperator[S, A, B]

LensComposeIso converts a Lens to a property of `A` into a lens to a property of type `B` the transformation is done via an ISO

func LensComposeLens

func LensComposeLens[S, A, B any](ab Lens[A, B]) LensOperator[S, A, B]

LensComposeLens combines two lenses to focus on a deeply nested field.

Given a lens from S to A and a lens from A to B, LensComposeLens creates a lens from S to B. This allows you to navigate through nested structures in a composable way.

The composition follows the mathematical property: (sa ∘ ab).Get = ab.Get ∘ sa.Get

Type Parameters:

  • S: Outer structure type
  • A: Intermediate structure type
  • B: Inner focus type

Parameters:

  • ab: Lens from A to B (inner lens)

Returns:

  • A function that takes a Lens[S, A] and returns a Lens[S, B]

Example:

type Address struct {
    Street string
    City   string
}

type Person struct {
    Name    string
    Address Address
}

addressLens := lens.MakeLens(
    func(p Person) Address { return p.Address },
    func(p Person, a Address) Person { p.Address = a; return p },
)

streetLens := lens.MakeLens(
    func(a Address) string { return a.Street },
    func(a Address, s string) Address { a.Street = s; return a },
)

// LensComposeLens to access street directly from person
personStreetLens := F.Pipe1(addressLens, lens.LensComposeLens[Person](streetLens))

person := Person{Name: "Alice", Address: Address{Street: "Main St"}}
street := personStreetLens.Get(person)  // "Main St"
updated := personStreetLens.Set("Oak Ave")(person)

func LensComposeLensRef deprecated

func LensComposeLensRef[S, A, B any](ab Lens[A, B]) LensOperator[*S, A, B]

LensComposeLensRef combines two lenses for pointer-based structures.

Deprecated: LensComposeLensRef is not needed. When the outer lens is already a pointer lens (Lens[*S, A], created with MakeLensRef or MakeLensRefCurried), its Set implementation already copies *S before writing. The copy that LensComposeLensRef adds via MakeLensRefCurriedWithName is therefore redundant — the composed setter calls the outer lens's Set, which copies, so the original pointer is never mutated.

Use LensComposeLens[*S] instead:

// Before
personStreetLens := F.Pipe1(addressLens, lens.LensComposeLensRef[Person](streetLens))

// After — identical behaviour, no extra copy
personStreetLens := F.Pipe1(addressLens, lens.Compose[*Person](streetLens))

Type Parameters:

  • S: Outer structure type (will be used as *S)
  • A: Intermediate structure type
  • B: Inner focus type

Parameters:

  • ab: Lens from A to B (inner lens)

Returns:

  • A function that takes a Lens[*S, A] and returns a Lens[*S, B]

func LensIMap

func LensIMap[S any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) LensOperator[S, A, B]

LensIMap transforms the focus type of a lens using an isomorphism.

An isomorphism is a pair of functions (A → B, B → A) that are inverses of each other. LensIMap allows you to work with a lens in a different but equivalent type. This is useful for unit conversions, encoding/decoding, or any bidirectional transformation.

Type Parameters:

  • E: Structure type
  • AB: Forward transformation function type (A → B)
  • BA: Backward transformation function type (B → A)
  • A: Original focus type
  • B: Transformed focus type

Parameters:

  • ab: Forward transformation (A → B)
  • ba: Backward transformation (B → A)

Returns:

  • A function that takes a Lens[E, A] and returns a Lens[E, B]

Example:

type Celsius float64
type Fahrenheit float64

celsiusToFahrenheit := func(c Celsius) Fahrenheit {
    return Fahrenheit(c*9/5 + 32)
}

fahrenheitToCelsius := func(f Fahrenheit) Celsius {
    return Celsius((f - 32) * 5 / 9)
}

type Weather struct {
    Temperature Celsius
}

tempCelsiusLens := lens.MakeLens(
    func(w Weather) Celsius { return w.Temperature },
    func(w Weather, t Celsius) Weather { w.Temperature = t; return w },
)

// Create a lens that works with Fahrenheit
tempFahrenheitLens := F.Pipe1(
    tempCelsiusLens,
    lens.LensIMap[Weather](celsiusToFahrenheit, fahrenheitToCelsius),
)

weather := Weather{Temperature: 20} // 20°C
tempF := tempFahrenheitLens.Get(weather)  // 68°F
updated := tempFahrenheitLens.Set(86)(weather)  // Set to 86°F (30°C)

type Option

type Option[A any] = option.Option[A]

type Optional

type Optional[S, A any] struct {
	GetOption func(s S) O.Option[A]
	Set       func(a A) EM.Endomorphism[S]
	// contains filtered or unexported fields
}

Optional is an optional reference to a subpart of a data type

func MakeOptional

func MakeOptional[S, A any](get O.Kleisli[S, A], set func(S, A) S) Optional[S, A]

MakeOptional creates an Optional based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the data. This happens automatically if the data is passed by value. For pointers consider to use `MakeOptionalRef` and for other kinds of data structures that are copied by reference make sure the setter creates the copy.

func MakeOptionalCurried

func MakeOptionalCurried[S, A any](get O.Kleisli[S, A], set func(A) func(S) S) Optional[S, A]

func MakeOptionalCurriedWithName

func MakeOptionalCurriedWithName[S, A any](get O.Kleisli[S, A], set func(A) func(S) S, name string) Optional[S, A]

func MakeOptionalRef

func MakeOptionalRef[S, A any](get O.Kleisli[*S, A], set func(*S, A) *S) Optional[*S, A]

MakeOptionalRef creates an Optional based on a getter and a setter function. The setter passed in does not have to create a shallow copy, the implementation wraps the setter into one that copies the pointer before modifying it

func MakeOptionalRefCurriedWithName

func MakeOptionalRefCurriedWithName[S, A any](get O.Kleisli[*S, A], set func(A) func(*S) *S, name string) Optional[*S, A]

func MakeOptionalRefWithName

func MakeOptionalRefWithName[S, A any](get O.Kleisli[*S, A], set func(*S, A) *S, name string) Optional[*S, A]

func MakeOptionalWithName

func MakeOptionalWithName[S, A any](get O.Kleisli[S, A], set func(S, A) S, name string) Optional[S, A]

func OptionalId

func OptionalId[S any]() Optional[S, S]

OptionalId returns am optional implementing the identity operation

func OptionalIdRef

func OptionalIdRef[S any]() Optional[*S, *S]

Id returns am optional implementing the identity operation

func (Optional[S, A]) Compose

func (p Optional[S, A]) Compose[B any](ab Optional[A, B]) Optional[S, B]

Compose returns a new Optional that focuses deeper into a structure by chaining this optional (S → A) with an inner optional (A → B), producing a composed optional (S → B).

This is the method-receiver form of the package-level OptionalComposeOptional function, available only on Go 1.27 and later because Go did not support type parameters on methods before that version. On earlier toolchains, use the equivalent free function instead:

// method form (go1.27+)
composed := outerOptional.Compose(innerOptional)

// free-function form (all versions)
composed := optional.Compose[S](innerOptional)(outerOptional)

GetOption of the composed optional chains the two GetOption functions: it first applies the outer optional to obtain an Option[A], then chains the inner optional through that option via O.Chain, returning None whenever either optional produces None.

Set of the composed optional updates the deeply nested focus by threading the new value back through the inner optional's Set and then propagating the change upward via the outer optional's Set. When either optional produces None, Set is a no-op and the original S is returned unchanged, consistent with the Optional no-op law.

The composed optional satisfies all three optional laws (GetSet, SetGet, SetSet) whenever both constituent optionals individually satisfy them.

Type Parameters:

  • B: the focus type of the inner optional and of the resulting optional

Parameters:

  • ab: the inner optional from A to B

Returns:

  • Optional[S, B]: a new optional from S directly to B

See Also:

  • Compose: the equivalent package-level function
  • ComposeRef: variant for pointer-based outer structures

func (Optional) Format

func (o Optional) Format(f fmt.State, c rune)

func (Optional) LogValue

func (o Optional) LogValue() slog.Value

func (Optional) String

func (o Optional) String() string

String returns the name of the optional for debugging and display purposes.

Example:

fieldOptional := optional.MakeOptionalWithName(..., "Person.Email")
fmt.Println(fieldOptional)  // Prints: "Person.Email"

type OptionalKleisli

type OptionalKleisli[S, A, B any] = func(A) Optional[S, B]

OptionalKleisli represents a function that takes a value of type A and returns an Optional[S, B]. This is commonly used for composing optionals in a monadic style.

Type Parameters:

  • S: The source type of the resulting optional
  • A: The input type to the function
  • B: The focus type of the resulting optional

type OptionalOperator

type OptionalOperator[S, A, B any] = func(Optional[S, A]) Optional[S, B]

OptionalOperator represents a function that transforms one optional into another. It takes an Optional[S, A] and returns an Optional[S, B], allowing for optional transformations.

Type Parameters:

  • S: The source type (remains constant)
  • A: The original focus type
  • B: The new focus type

func OptionalComposeOptional

func OptionalComposeOptional[S, A, B any](ab Optional[A, B]) OptionalOperator[S, A, B]

OptionalComposeOptional combines two Optional and allows to narrow down the focus to a sub-Optional

func OptionalComposeOptionalRef

func OptionalComposeOptionalRef[S, A, B any](ab Optional[A, B]) OptionalOperator[*S, A, B]

OptionalComposeOptionalRef combines two Optional and allows to narrow down the focus to a sub-Optional

func OptionalIChain

func OptionalIChain[S, A, B any](ab O.Kleisli[A, B], ba O.Kleisli[B, A]) OptionalOperator[S, A, B]

OptionalIChain implements a bidirectional mapping of the transform if the transform can produce optionals (e.g. in case of type mappings)

func OptionalIChainAny

func OptionalIChainAny[S, A any]() OptionalOperator[S, any, A]

OptionalIChainAny implements a bidirectional mapping to and from any

func OptionalIMap

func OptionalIMap[S, A, B any](ab func(A) B, ba func(B) A) OptionalOperator[S, A, B]

OptionalIMap implements a bidirectional mapping of the transform

type Prism

type Prism[S, A any] struct {

	// GetOption attempts to extract a value of type A from S.
	// Returns Some(a) if the extraction succeeds, None otherwise.
	GetOption O.Kleisli[S, A]

	// ReverseGet constructs an S from an A.
	// This operation always succeeds.
	ReverseGet func(A) S
	// contains filtered or unexported fields
}

Prism is an optic used to select part of a sum type (tagged union). It provides two operations:

  • GetOption: Try to extract a value of type A from S (may fail)
  • ReverseGet: Construct an S from an A (always succeeds)

Prisms are useful for working with variant types like Either, Option, or custom sum types where you want to focus on a specific variant.

Type Parameters:

  • S: The source type (sum type)
  • A: The focus type (variant within the sum type)

Example:

type Result interface{ isResult() }
type Success struct{ Value int }
type Failure struct{ Error string }

successPrism := MakePrism(
    func(r Result) Option[int] {
        if s, ok := r.(Success); ok {
            return Some(s.Value)
        }
        return None[int]()
    },
    func(v int) Result { return Success{Value: v} },
)

func MakePrism

func MakePrism[S, A any](get O.Kleisli[S, A], rev func(A) S) Prism[S, A]

MakePrism constructs a Prism from GetOption and ReverseGet functions.

Parameters:

  • get: Function to extract A from S (returns Option[A])
  • rev: Function to construct S from A

Returns:

  • A Prism[S, A] that uses the provided functions

Example:

prism := MakePrism(
    func(opt Option[int]) Option[int] { return opt },
    func(n int) Option[int] { return Some(n) },
)

func MakePrismWithName

func MakePrismWithName[S, A any](get O.Kleisli[S, A], rev func(A) S, name string) Prism[S, A]

func PrismFromOption

func PrismFromOption[T any]() Prism[Option[T], T]

PrismFromOption creates a prism for extracting values from Option types. It provides a safe way to work with Option values, focusing on the Some case and handling the None case gracefully through the prism's GetOption behavior.

The prism's GetOption is the identity function - it returns the Option as-is. If the Option is Some(value), GetOption returns Some(value); if it's None, it returns None. This allows the prism to naturally handle the presence or absence of a value.

The prism's ReverseGet wraps a value into Some, always succeeding.

Type Parameters:

  • T: The value type contained in the Option

Returns:

  • A Prism[Option[T], T] that safely extracts values from Options

Example:

// Create a prism for extracting int values from Option[int]
optPrism := FromOption[int]()

// Extract from Some
someValue := option.Some(42)
result := optPrism.GetOption(someValue)  // Some(42)

// Extract from None
noneValue := option.None[int]()
result = optPrism.GetOption(noneValue)  // None[int]()

// Wrap value into Some
wrapped := optPrism.ReverseGet(100)  // Some(100)

// Use with Set to update Some values
setter := Set[Option[int], int](200)
result := setter(optPrism)(someValue)  // Some(200)
result = setter(optPrism)(noneValue)   // None[int]() (unchanged)

// Compose with other prisms for nested extraction
// Extract int from Option[Option[int]]
nestedPrism := Compose[Option[Option[int]], Option[int], int](
    FromOption[Option[int]](),
    FromOption[int](),
)
nested := option.Some(option.Some(42))
value := nestedPrism.GetOption(nested)  // Some(42)

Common use cases:

  • Extracting values from optional fields
  • Working with nullable data in a type-safe way
  • Composing with other prisms to handle nested Options
  • Filtering and transforming optional values in pipelines
  • Converting between Option and other optional representations

Key insight: This prism treats Option[T] as a "container" that may or may not hold a value of type T. The prism focuses on the value inside, allowing you to work with it when present and gracefully handle its absence when not.

func PrismFromPredicate

func PrismFromPredicate[S any](pred func(S) bool) Prism[S, S]

PrismFromPredicate creates a prism that matches values satisfying a predicate. GetOption returns Some(s) if the predicate is true, None otherwise. ReverseGet is the identity function (doesn't validate the predicate).

Parameters:

  • pred: Predicate function to test values

Returns:

  • A Prism[S, S] that filters based on the predicate

Example:

positivePrism := PrismFromPredicate(N.MoreThan(0))
value := positivePrism.GetOption(42)  // Some(42)
value = positivePrism.GetOption(-5)   // None[int]

func PrismId

func PrismId[S any]() Prism[S, S]

PrismId returns an identity prism that focuses on the entire value. GetOption always returns Some(s), and ReverseGet is the identity function.

This is useful as a starting point for prism composition or when you need a prism that doesn't actually transform the value.

Example:

idPrism := PrismId[int]()
value := idPrism.GetOption(42)    // Some(42)
result := idPrism.ReverseGet(42)  // 42

func PrismSome

func PrismSome[S, A any](soa Prism[S, Option[A]]) Prism[S, A]

PrismSome creates a prism that focuses on the PrismSome variant of an Option within a structure. It composes the provided prism (which focuses on an Option[A]) with a prism that extracts the value from PrismSome.

Type Parameters:

  • S: The source type
  • A: The value type within the Option

Parameters:

  • soa: A prism that focuses on an Option[A] within S

Returns:

  • A prism that focuses on the A value within PrismSome

Example:

type Config struct { Timeout Option[int] }
configPrism := MakePrism(...)  // Prism[Config, Option[int]]
timeoutPrism := PrismSome(configPrism)  // Prism[Config, int]
value := timeoutPrism.GetOption(Config{Timeout: PrismSome(30)})  // PrismSome(30)

func (Prism[S, A]) Compose

func (p Prism[S, A]) Compose[B any](ab Prism[A, B]) Prism[S, B]

Compose returns a new prism that focuses deeper into a sum type by chaining this prism (S → A) with an inner prism (A → B), producing a composed prism (S → B).

This is the method-receiver form of the package-level OptionalComposeOptional function, available only on Go 1.27 and later because Go did not support type parameters on methods before that version. On earlier toolchains, use the equivalent free function instead:

// method form (go1.27+)
composed := outerPrism.Compose(innerPrism)

// free-function form (all versions)
composed := prism.Compose[S](innerPrism)(outerPrism)

GetOption of the composed prism chains the two GetOption functions: it first applies the outer prism to obtain an Option[A], then chains the inner prism through that option via O.Chain, returning None whenever either prism fails to match.

ReverseGet of the composed prism pipes the value back through both ReverseGet functions in order: inner first, then outer.

The composed prism satisfies both prism laws whenever both constituent prisms individually satisfy them.

Type Parameters:

  • B: the focus type of the inner prism and of the resulting prism

Parameters:

  • ab: the inner prism from A to B

Returns:

  • Prism[S, B]: a new prism from S directly to B

See Also:

  • Compose: the equivalent package-level function

func (Prism) Format

func (l Prism) Format(f fmt.State, c rune)

Format implements fmt.Formatter.

Supports all standard format verbs:

  • %s, %v, %+v, %q, and all other verbs: uses the String() representation (the prism name)

func (Prism) LogValue

func (l Prism) LogValue() slog.Value

LogValue implements slog.LogValuer.

Returns a slog.Value that represents the prism for structured logging. The prism name is logged as a string value.

func (Prism) String

func (l Prism) String() string

String returns the name of the prism for debugging and display purposes.

type PrismKleisli

type PrismKleisli[S, A, B any] = func(A) Prism[S, B]

Kleisli represents a function that takes a value of type A and returns a Prism[S, B]. This is commonly used for composing prisms in a monadic style.

Type Parameters:

  • S: The source type of the resulting prism
  • A: The input type to the function
  • B: The focus type of the resulting prism

type PrismOperator

type PrismOperator[S, A, B any] = func(Prism[S, A]) Prism[S, B]

Operator represents a function that transforms one prism into another. It takes a Prism[S, A] and returns a Prism[S, B], allowing for prism transformations.

Type Parameters:

  • S: The source type (remains constant)
  • A: The original focus type
  • B: The new focus type

func PrismComposePrism

func PrismComposePrism[S, A, B any](ab Prism[A, B]) PrismOperator[S, A, B]

Compose composes two prisms to create a prism that focuses deeper into a structure. The resulting prism first applies the outer prism (S → A), then the inner prism (A → B).

Type Parameters:

  • S: The outermost source type
  • A: The intermediate type
  • B: The innermost focus type

Parameters:

  • ab: The inner prism (A → B)

Returns:

  • A function that takes the outer prism (S → A) and returns the composed prism (S → B)

Example:

outerPrism := MakePrism(...)  // Prism[Outer, Inner]
innerPrism := MakePrism(...)  // Prism[Inner, Value]
composed := Compose[Outer](innerPrism)(outerPrism)  // Prism[Outer, Value]

func PrismIMap

func PrismIMap[S any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) PrismOperator[S, A, B]

PrismIMap bidirectionally maps the focus type of a prism. It transforms a Prism[S, A] into a Prism[S, B] using two functions: one to map A → B and another to map B → A.

Type Parameters:

  • S: The source type
  • A: The original focus type
  • B: The new focus type
  • AB: Function type A → B
  • BA: Function type B → A

Parameters:

  • ab: Function to map from A to B
  • ba: Function to map from B to A

Returns:

  • A function that transforms Prism[S, A] to Prism[S, B]

Example:

intPrism := MakePrism(...)  // Prism[Result, int]
stringPrism := PrismIMap[Result](
    strconv.Itoa,
    func(s string) int { n, _ := strconv.Atoi(s); return n },
)(intPrism)  // Prism[Result, string]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL