lens

package
v2.2.21 Latest Latest
Warning

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

Go to latest
Published: Feb 24, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package lens provides functional optics for zooming into and modifying nested data structures.

Overview

A Lens is a first-class reference to a subpart of a data structure. It provides a composable way to focus on a particular field within a nested structure, allowing you to get and set values in an immutable, functional manner.

Lenses are particularly useful when working with deeply nested immutable data structures, as they eliminate the need for verbose copying and updating code.

Mathematical Foundation

A Lens[S, A] is defined by two operations:

  • Get: S → A (extract a value of type A from a structure of type S)
  • Set: A → S → S (update the value of type A in structure S, returning a new S)

Lenses must satisfy the lens laws:

  1. GetSet: lens.Set(lens.Get(s))(s) == s
  2. SetGet: lens.Get(lens.Set(a)(s)) == a
  3. SetSet: lens.Set(a2)(lens.Set(a1)(s)) == lens.Set(a2)(s)

Basic Usage

Creating a lens for a struct field:

type Person struct {
	Name string
	Age  int
}

// Create a lens for the Name field
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}

// Get the name
name := nameLens.Get(person) // "Alice"

// Set a new name (returns a new Person)
updated := nameLens.Set("Bob")(person)
// person.Name is still "Alice", updated.Name is "Bob"

Composing Lenses

Lenses can be composed to focus on deeply nested fields:

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
	},
)

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

person := Person{
	Name: "Alice",
	Address: Address{Street: "Main St", City: "NYC"},
}

street := personStreetLens.Get(person) // "Main St"
updated := personStreetLens.Set("Oak Ave")(person)

Working with Pointers

For pointer-based structures, use MakeLensRef which handles copying automatically:

type Person struct {
	Name string
	Age  int
}

func (p *Person) GetName() string {
	return p.Name
}

func (p *Person) SetName(name string) *Person {
	p.Name = name
	return p
}

// MakeLensRef handles pointer copying
nameLens := lens.MakeLensRef(
	(*Person).GetName,
	(*Person).SetName,
)

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

Optional Values

Lenses can work with optional values using Option types:

type Config struct {
	Port    *int
	Timeout *int
}

portLens := lens.MakeLens(
	func(c Config) *int { return c.Port },
	func(c Config, p *int) Config {
		c.Port = p
		return c
	},
)

// Convert to optional lens
optPortLens := lens.FromNillable(portLens)

config := Config{Port: nil}

// Get returns None for nil
port := optPortLens.Get(config) // None[*int]

// Set with Some updates the value
newPort := 8080
updated := optPortLens.Set(O.Some(&newPort))(config)

// Set with None removes the value
cleared := optPortLens.Set(O.None[*int]())(updated)

Composing with Optional Values

ComposeOption allows composing a lens returning an optional value with a regular lens:

type Database struct {
	Host string
	Port int
}

type Config struct {
	Database *Database
}

dbLens := lens.FromNillable(lens.MakeLens(
	func(c Config) *Database { return c.Database },
	func(c Config, db *Database) Config {
		c.Database = db
		return c
	},
))

portLens := lens.MakeLensRef(
	func(db *Database) int { return db.Port },
	func(db *Database, port int) *Database {
		db.Port = port
		return db
	},
)

defaultDB := &Database{Host: "localhost", Port: 5432}

// Compose with default value
configPortLens := F.Pipe1(
	dbLens,
	lens.ComposeOption[Config, int](defaultDB)(portLens),
)

config := Config{Database: nil}

// Get returns None when database is nil
port := configPortLens.Get(config) // None[int]

// Set creates database with default values
updated := configPortLens.Set(O.Some(3306))(config)
// updated.Database.Port == 3306, Host == "localhost"

Modifying Values

Use Modify to transform a value through a lens:

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(
	counter,
	valueLens,
	lens.Modify[Counter](func(v int) int { return v + 1 }),
)
// incremented.Value == 6

Identity Lens

The identity lens focuses on the entire structure:

idLens := lens.Id[Person]()

person := Person{Name: "Alice", Age: 30}
same := idLens.Get(person) // returns person
updated := idLens.Set(Person{Name: "Bob", Age: 25})(person)

Isomorphic Mapping

IMap allows you to transform the focus type of a lens:

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.IMap[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)

Nullable Properties

FromNullableProp creates a lens that provides a default value for nullable properties:

type Config struct {
	Timeout *int
}

timeoutLens := lens.MakeLens(
	func(c Config) *int { return c.Timeout },
	func(c Config, t *int) Config {
		c.Timeout = t
		return c
	},
)

// Provide default value of 30 for nil timeout
safeTimeoutLens := F.Pipe1(
	timeoutLens,
	lens.FromNullableProp[Config](
		O.FromNillable[int],
		func() *int { v := 30; return &v }(),
	),
)

config := Config{Timeout: nil}
timeout := safeTimeoutLens.Get(config) // returns pointer to 30

FromOption

FromOption creates a lens from an Option property, providing a default value:

type Settings struct {
	MaxRetries O.Option[int]
}

retriesLens := lens.MakeLens(
	func(s Settings) O.Option[int] { return s.MaxRetries },
	func(s Settings, r O.Option[int]) Settings {
		s.MaxRetries = r
		return s
	},
)

// Provide default of 3 retries
safeRetriesLens := F.Pipe1(
	retriesLens,
	lens.FromOption[Settings](3),
)

settings := Settings{MaxRetries: O.None[int]()}
retries := safeRetriesLens.Get(settings) // returns 3
updated := safeRetriesLens.Set(5)(settings) // sets to Some(5)

Predicate-Based Lenses

FromPredicate creates an optional lens based on a predicate:

type User struct {
	Age int
}

ageLens := lens.MakeLens(
	func(u User) int { return u.Age },
	func(u User, age int) User {
		u.Age = age
		return u
	},
)

// Only consider valid ages (18+)
adultAgeLens := F.Pipe1(
	ageLens,
	lens.FromPredicate[User](func(age int) bool {
		return age >= 18
	}, 0),
)

user := User{Age: 25}
age := adultAgeLens.Get(user) // Some(25)

minor := User{Age: 15}
minorAge := adultAgeLens.Get(minor) // None[int]

Real-World Example: Configuration Management

type DatabaseConfig struct {
	Host     string
	Port     int
	Username string
	Password string
}

type CacheConfig struct {
	TTL     int
	MaxSize int
}

type AppConfig struct {
	Database *DatabaseConfig
	Cache    *CacheConfig
	Debug    bool
}

// Create lenses for each level
dbLens := lens.FromNillable(lens.MakeLens(
	func(c AppConfig) *DatabaseConfig { return c.Database },
	func(c AppConfig, db *DatabaseConfig) AppConfig {
		c.Database = db
		return c
	},
))

dbHostLens := lens.MakeLensRef(
	func(db *DatabaseConfig) string { return db.Host },
	func(db *DatabaseConfig, host string) *DatabaseConfig {
		db.Host = host
		return db
	},
)

defaultDB := &DatabaseConfig{
	Host:     "localhost",
	Port:     5432,
	Username: "admin",
	Password: "",
}

// Compose to access database host from app config
appDbHostLens := F.Pipe1(
	dbLens,
	lens.ComposeOption[AppConfig, string](defaultDB)(dbHostLens),
)

config := AppConfig{Database: nil, Debug: true}

// Get returns None when database is not configured
host := appDbHostLens.Get(config) // None[string]

// Set creates database with default values
updated := appDbHostLens.Set(O.Some("prod.example.com"))(config)
// updated.Database.Host == "prod.example.com"
// updated.Database.Port == 5432 (from default)

Performance Considerations

Lenses create new copies of data structures on each Set operation. For deeply nested structures, this can be expensive. Consider:

1. Using pointer-based structures with MakeLensRef for better performance 2. Batching multiple updates using Modify 3. Using specialized lenses for common patterns (arrays, records, etc.)

Type Safety

Lenses are fully type-safe. The compiler ensures that: - Get returns the correct type - Set accepts the correct type - Composed lenses maintain type relationships

Function Reference

Core Lens Creation:

  • MakeLens: Create a lens from getter and setter functions
  • MakeLensCurried: Create a lens with curried setter
  • MakeLensRef: Create a lens for pointer-based structures
  • MakeLensRefCurried: Create a lens for pointers with curried setter
  • MakeLensWithEq: Create a lens with equality optimization for pointer structures
  • MakeLensStrict: Create a lens with strict equality optimization for pointer structures
  • Id: Create an identity lens
  • IdRef: Create an identity lens for pointers

Composition:

  • Compose: Compose two lenses
  • ComposeRef: Compose lenses for pointer structures
  • ComposeOption: Compose lens returning Option with regular lens
  • ComposeOptions: Compose two lenses returning Options

Transformation:

  • Modify: Transform a value through a lens
  • IMap: Transform the focus type of a lens

Optional Value Handling:

  • FromNillable: Create optional lens from nullable pointer
  • FromNillableRef: Create optional lens from nullable pointer (ref version)
  • FromNullableProp: Create lens with default for nullable property
  • FromNullablePropRef: Create lens with default for nullable property (ref version)
  • FromOption: Create lens from Option property with default
  • FromOptionRef: Create lens from Option property with default (ref version)
  • FromPredicate: Create optional lens based on predicate
  • FromPredicateRef: Create optional lens based on predicate (ref version)
  • github.com/IBM/fp-go/v2/optics/iso: Isomorphisms (bidirectional transformations)
  • github.com/IBM/fp-go/v2/optics/prism: Prisms (focus on sum types)
  • github.com/IBM/fp-go/v2/optics/optional: Optional optics
  • github.com/IBM/fp-go/v2/optics/traversal: Traversals (focus on multiple values)
  • github.com/IBM/fp-go/v2/option: Optional values
  • github.com/IBM/fp-go/v2/endomorphism: Endomorphisms (A → A functions)

Lens is an optic used to zoom inside a product.

Package lens provides functional optics for zooming into and updating parts of immutable data structures.

A lens is a composable, first-class reference to a subpart of a data structure that enables getting and setting values in a purely functional way without mutation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Modify

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

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

Instead of setting a specific value, Modify 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.Modify[Counter](func(v int) int { return v + 1 }),
    F.Ap(counter),
)
// incremented.Value == 6

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

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 Kleisli

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

Kleisli 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 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 Id

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

Id 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.Id[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 IdRef

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

IdRef returns an identity Lens for pointer-based structures.

This is the pointer version of Id. 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.IdRef[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 added in v2.1.20

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

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, T]) Format

func (l Lens[S, T]) Format(f fmt.State, c rune)

Format implements fmt.Formatter for Lens. Supports all standard format verbs:

  • %s, %v, %+v: uses String() representation (lens name)
  • %#v: uses GoString() representation
  • %q: quoted String() representation
  • other verbs: uses String() representation

Example:

nameLens := lens.MakeLensWithName(..., "Person.Name")
fmt.Printf("%s", nameLens)   // "Person.Name"
fmt.Printf("%v", nameLens)   // "Person.Name"
fmt.Printf("%#v", nameLens)  // "lens.Lens[Person, string]{name: \"Person.Name\"}"

func (Lens[S, T]) GoString

func (l Lens[S, T]) GoString() string

GoString implements fmt.GoStringer for Lens. Returns a Go-syntax representation of the Lens value.

Example:

nameLens := lens.MakeLensWithName(..., "Person.Name")
nameLens.GoString() // "lens.Lens[Person, string]{name: \"Person.Name\"}"

func (Lens[S, T]) LogValue

func (l Lens[S, T]) LogValue() slog.Value

LogValue implements slog.LogValuer for Lens. Returns a slog.Value that represents the Lens for structured logging. Logs the lens name as a string value.

Example:

logger := slog.Default()
nameLens := lens.MakeLensWithName(..., "Person.Name")
logger.Info("using lens", "lens", nameLens)
// Logs: {"msg":"using lens","lens":"Person.Name"}

func (Lens[S, T]) String

func (l Lens[S, T]) String() string

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

Example:

nameLens := lens.MakeLensWithName(..., "Person.Name")
fmt.Println(nameLens)  // Prints: "Person.Name"

type Operator

type Operator[S, A, B any] = Kleisli[S, Lens[S, A], B]

Operator 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 Compose

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

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

Given a lens from S to A and a lens from A to B, Compose 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 },
)

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

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

func ComposeRef

func ComposeRef[S, A, B any](ab Lens[A, B]) Operator[*S, A, B]

ComposeRef combines two lenses for pointer-based structures.

This is the pointer version of Compose, automatically handling copying to ensure immutability. It allows you to navigate through nested pointer structures in a composable way.

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]

Example:

type Address struct {
    Street string
}

type Person struct {
    Name    string
    Address Address
}

addressLens := lens.MakeLensRef(
    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 },
)

personStreetLens := F.Pipe1(addressLens, lens.ComposeRef[Person](streetLens))

func IMap

func IMap[S any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) Operator[S, A, B]

IMap 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. IMap 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.IMap[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)

Directories

Path Synopsis
Package iso provides utilities for composing lenses with isomorphisms.
Package iso provides utilities for composing lenses with isomorphisms.
Package option provides utilities for working with lenses that focus on optional values.
Package option provides utilities for working with lenses that focus on optional values.

Jump to

Keyboard shortcuts

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