Documentation
¶
Overview ¶
Package option provides utilities for working with lenses that focus on optional values.
This package extends the lens optics pattern to handle Option types, enabling safe manipulation of potentially absent values in nested data structures. It provides functions for creating, composing, and transforming lenses that work with optional fields.
Core Concepts ¶
A LensO[S, A] is a Lens[S, Option[A]] - a lens that focuses on an optional value A within a structure S. This is particularly useful when dealing with nullable pointers, optional fields, or values that may not always be present.
Key Functions ¶
Creating Lenses from Optional Values:
- FromNillable: Creates a lens from a nullable pointer field
- FromNillableRef: Pointer-based version of FromNillable
- FromPredicate: Creates a lens based on a predicate function
- FromPredicateRef: Pointer-based version of FromPredicate
- FromOption: Converts an optional lens to a definite lens with a default value
- FromOptionRef: Pointer-based version of FromOption
- FromNullableProp: Creates a lens with a default value for nullable properties
- FromNullablePropRef: Pointer-based version of FromNullableProp
Composing Lenses:
- ComposeOption: Composes a lens returning Option[A] with a lens returning B
- ComposeOptions: Composes two lenses that both return optional values
Conversions:
- AsTraversal: Converts a lens to a traversal for use with traversal operations
Usage Examples ¶
Working with nullable pointers:
type Config struct {
Database *DatabaseConfig
}
type DatabaseConfig struct {
Host string
Port int
}
// Create a lens for the optional database config
dbLens := lens.FromNillable(lens.MakeLens(
func(c Config) *DatabaseConfig { return c.Database },
func(c Config, db *DatabaseConfig) Config { c.Database = db; return c },
))
// Access the optional value
config := Config{Database: nil}
dbOpt := dbLens.Get(config) // Returns None[*DatabaseConfig]
// Set a value
newDB := &DatabaseConfig{Host: "localhost", Port: 5432}
updated := dbLens.Set(O.Some(newDB))(config)
Composing optional lenses:
// Lens to access port through optional database
portLens := lens.MakeLensRef(
func(db *DatabaseConfig) int { return db.Port },
func(db *DatabaseConfig, port int) *DatabaseConfig { db.Port = port; return db },
)
defaultDB := &DatabaseConfig{Host: "localhost", Port: 5432}
configPortLens := F.Pipe1(dbLens,
lens.ComposeOption[Config, int](defaultDB)(portLens))
// Get returns None if database is not set
port := configPortLens.Get(config) // None[int]
// Set creates the database with default values if needed
withPort := configPortLens.Set(O.Some(3306))(config)
// withPort.Database.Port == 3306, Host == "localhost"
Working with predicates:
type Person struct {
Age int
}
ageLens := lens.MakeLensRef(
func(p *Person) int { return p.Age },
func(p *Person, age int) *Person { p.Age = age; return p },
)
// Only consider adults (age >= 18)
adultLens := lens.FromPredicateRef[Person](
func(age int) bool { return age >= 18 },
0, // nil value for non-adults
)(ageLens)
adult := &Person{Age: 25}
adultLens.Get(adult) // Some(25)
minor := &Person{Age: 15}
adultLens.Get(minor) // None[int]
Design Patterns ¶
The package follows functional programming principles:
- Immutability: All operations return new values rather than modifying in place
- Composition: Lenses can be composed to access deeply nested optional values
- Type Safety: The type system ensures correct usage at compile time
- Lawful: All lenses satisfy the lens laws (get-put, put-get, put-put)
Performance Considerations ¶
Lens operations are generally efficient, but composing many lenses can create function call overhead. For performance-critical code, consider:
- Caching composed lenses rather than recreating them
- Using direct field access for simple cases
- Profiling to identify bottlenecks
Related Packages ¶
- github.com/IBM/fp-go/v2/optics/lens: Core lens functionality
- github.com/IBM/fp-go/v2/option: Option type and operations
- github.com/IBM/fp-go/v2/optics/traversal/option: Traversals for optional values
Index ¶
- func AsTraversal[S, A any]() func(Lens[S, A]) T.Traversal[S, A]
- func Compose[S, B, A any](defaultA A) func(LensO[A, B]) Operator[S, A, B]
- func ComposeOption[S, B, A any](defaultA A) func(Lens[A, B]) Operator[S, A, B]
- func FromIso[S, A any](iso Iso[A, Option[A]]) func(Lens[S, A]) LensO[S, A]
- func FromNullableProp[S, A any](isNullable O.Kleisli[A, A], defaultValue A) lens.Operator[S, A, A]
- func FromNullablePropRef[S, A any](isNullable O.Kleisli[A, A], defaultValue A) lens.Operator[*S, A, A]
- func FromOption[S, A any](defaultValue A) func(LensO[S, A]) Lens[S, A]
- func FromOptionRef[S, A any](defaultValue A) func(LensO[*S, A]) Lens[*S, A]
- func FromPredicate[S, A any](pred func(A) bool, nilValue A) func(sa Lens[S, A]) LensO[S, A]
- func FromPredicateRef[S, A any](pred func(A) bool, nilValue A) func(sa Lens[*S, A]) LensO[*S, A]
- type Endomorphism
- type Iso
- type Kleisli
- type Lens
- type LensO
- type Operator
- type Option
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AsTraversal ¶
AsTraversal converts a Lens[S, A] to a Traversal[S, A] for optional values.
A traversal is a generalization of a lens that can focus on zero or more values. This function converts a lens (which focuses on exactly one value) into a traversal, allowing it to be used with traversal operations like mapping over multiple values.
This is particularly useful when you want to:
- Use lens operations in a traversal context
- Compose lenses with traversals
- Apply operations that work on collections of optional values
The conversion uses the Option monad's map operation to handle the optional nature of the values being traversed.
Type Parameters:
- S: The structure type containing the field
- A: The type of the field being focused on
Returns:
- A function that takes a Lens[S, A] and returns a Traversal[S, A]
Example:
type Config struct {
Timeout Option[int]
}
timeoutLens := lens.MakeLens(
func(c Config) Option[int] { return c.Timeout },
func(c Config, t Option[int]) Config { c.Timeout = t; return c },
)
// Convert to traversal for use with traversal operations
timeoutTraversal := lens.AsTraversal[Config, int]()(timeoutLens)
// Now can use traversal operations
configs := []Config{{Timeout: O.Some(30)}, {Timeout: O.None[int]()}}
// Apply operations across all configs using the traversal
func Compose ¶
func Compose[S, B, A any](defaultA A) func(LensO[A, B]) Operator[S, A, B]
Compose composes two lenses that both return optional values.
This handles the case where both the intermediate structure A and the inner focus B are optional. The getter returns None[B] if either A or B is None. The setter behavior is:
- Set(Some[B]) when A exists: Updates B in A
- Set(Some[B]) when A doesn't exist: Creates A with defaultA and sets B
- Set(None[B]) when A doesn't exist: Identity operation (no change)
- Set(None[B]) when A exists: Removes B from A (sets it to None)
Type Parameters:
- S: Outer structure type
- B: Inner focus type (optional)
- A: Intermediate structure type (optional)
Parameters:
- defaultA: Default value for A when it doesn't exist but B needs to be set
Returns:
- A function that takes a LensO[A, B] and returns a function that takes a LensO[S, A] and returns a LensO[S, B]
Example:
type Settings struct {
MaxRetries *int
}
type Config struct {
Settings *Settings
}
settingsLens := lens.FromNillable(lens.MakeLens(
func(c Config) *Settings { return c.Settings },
func(c Config, s *Settings) Config { c.Settings = s; return c },
))
retriesLens := lens.FromNillable(lens.MakeLensRef(
func(s *Settings) *int { return s.MaxRetries },
func(s *Settings, r *int) *Settings { s.MaxRetries = r; return s },
))
defaultSettings := &Settings{}
configRetriesLens := F.Pipe1(settingsLens,
lens.Compose[Config, *int](defaultSettings)(retriesLens))
func ComposeOption ¶
func ComposeOption[S, B, A any](defaultA A) func(Lens[A, B]) Operator[S, A, B]
ComposeOption composes a lens returning an optional value with a lens returning a definite value.
This is useful when you have an optional intermediate structure and want to focus on a field within it. The getter returns Option[B] because the container A might not exist. The setter behavior depends on the input:
- Set(Some[B]): Updates B in A, creating A with defaultA if it doesn't exist
- Set(None[B]): Removes A entirely (sets it to None[A])
Type Parameters:
- S: Outer structure type
- B: Inner focus type (definite value)
- A: Intermediate structure type (optional)
Parameters:
- defaultA: Default value for A when it doesn't exist but B needs to be set
Returns:
- A function that takes a Lens[A, B] and returns a function that takes a LensO[S, A] and returns a LensO[S, B]
Example:
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}
configPortLens := F.Pipe1(dbLens, lens.ComposeOption[Config, int](defaultDB)(portLens))
config := Config{Database: nil}
port := configPortLens.Get(config) // None[int]
updated := configPortLens.Set(O.Some(3306))(config)
// updated.Database.Port == 3306, Host == "localhost" (from default)
func FromIso ¶
func FromIso[S, A any](iso Iso[A, Option[A]]) func(Lens[S, A]) LensO[S, A]
FromIso converts a Lens[S, A] to a LensO[S, A] using an isomorphism.
This function takes an isomorphism between A and Option[A] and uses it to transform a regular lens into an optional lens. It's particularly useful when you have a custom isomorphism that defines how to convert between a value and its optional representation.
The isomorphism must satisfy the round-trip laws:
- iso.ReverseGet(iso.Get(a)) == a for all a: A
- iso.Get(iso.ReverseGet(opt)) == opt for all opt: Option[A]
Type Parameters:
- S: The structure type containing the field
- A: The type of the field being focused on
Parameters:
- iso: An isomorphism between A and Option[A] that defines the conversion
Returns:
- A function that takes a Lens[S, A] and returns a LensO[S, A]
Example:
type Config struct {
timeout int
}
// Create a lens to the timeout field
timeoutLens := lens.MakeLens(
func(c Config) int { return c.timeout },
func(c Config, t int) Config { c.timeout = t; return c },
)
// Create an isomorphism that treats 0 as None
zeroAsNone := iso.MakeIso(
func(t int) option.Option[int] {
if t == 0 {
return option.None[int]()
}
return option.Some(t)
},
func(opt option.Option[int]) int {
return option.GetOrElse(func() int { return 0 })(opt)
},
)
// Convert to optional lens
optTimeoutLens := FromIso[Config, int](zeroAsNone)(timeoutLens)
config := Config{timeout: 0}
opt := optTimeoutLens.Get(config) // None[int]()
updated := optTimeoutLens.Set(option.Some(30))(config) // Config{timeout: 30}
Common Use Cases:
- Converting between sentinel values (like 0, -1, "") and Option
- Applying custom validation logic when converting to/from Option
- Integrating with existing isomorphisms like FromNillable
See also:
- FromPredicate: For predicate-based optional conversion
- FromNillable: For pointer-based optional conversion
- FromOption: For converting from optional to non-optional with defaults
func FromNullableProp ¶
FromNullableProp returns a `Lens` from a property that may be optional. The getter returns a default value for these items
func FromNullablePropRef ¶
func FromNullablePropRef[S, A any](isNullable O.Kleisli[A, A], defaultValue A) lens.Operator[*S, A, A]
FromNullablePropRef returns a `Lens` from a property that may be optional. The getter returns a default value for these items
func FromOption ¶
func FromOption[S, A any](defaultValue A) func(LensO[S, A]) Lens[S, A]
FromOption returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option
func FromOptionRef ¶
func FromOptionRef[S, A any](defaultValue A) func(LensO[*S, A]) Lens[*S, A]
FromOptionRef creates a lens from an Option property with a default value for pointer structures.
This is the pointer version of FromOption, with automatic copying to ensure immutability. The getter returns the value inside Some[A], or the defaultValue if it's None[A]. The setter always wraps the value in Some[A].
Type Parameters:
- S: Structure type (will be used as *S)
- A: Focus type
Parameters:
- defaultValue: Value to return when the Option is None
Returns:
- A function that takes a Lens[*S, Option[A]] and returns a Lens[*S, A]
func FromPredicate ¶
FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional if the optional value is set then the nil value will be set instead
func FromPredicateRef ¶
FromPredicateRef returns a `Lens` for a property accessibly as a getter and setter that can be optional if the optional value is set then the nil value will be set instead
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.
This is commonly used in lens setters to transform a structure by applying a function that takes and returns the same type.
Example:
increment := N.Add(1) // increment is an Endomorphism[int]
type Iso ¶
Iso represents an isomorphism between types S and A. An isomorphism is a bidirectional transformation that preserves structure.
type Kleisli ¶
Kleisli represents a Kleisli arrow for optional lenses. It's a function from A to LensO[S, B], used for composing optional lens operations.
type Lens ¶
Lens represents a functional reference to a field within a structure.
A Lens[S, A] provides a way to get and set a value of type A within a structure of type S in an immutable way. It consists of:
- Get: S → A (retrieve the value)
- Set: A → S → S (update the value, returning a new structure)
Lenses satisfy three laws:
- Get-Put: lens.Set(lens.Get(s))(s) == s
- Put-Get: lens.Get(lens.Set(a)(s)) == a
- Put-Put: lens.Set(b)(lens.Set(a)(s)) == lens.Set(b)(s)
Type Parameters:
- S: The structure type containing the field
- A: The type of the field being focused on
type LensO ¶
LensO is a lens that focuses on an optional value.
A LensO[S, A] is equivalent to Lens[S, Option[A]], representing a lens that focuses on a value of type A that may or may not be present within a structure S.
This is particularly useful for:
- Nullable pointer fields
- Optional configuration values
- Fields that may be conditionally present
The getter returns Option[A] (Some if present, None if absent). The setter takes Option[A] (Some to set, None to remove).
Type Parameters:
- S: The structure type containing the optional field
- A: The type of the optional value being focused on
Example:
type Config struct {
Timeout *int
}
timeoutLens := lens.MakeLensRef(
func(c *Config) *int { return c.Timeout },
func(c *Config, t *int) *Config { c.Timeout = t; return c },
)
optLens := lens.FromNillableRef(timeoutLens)
// optLens is a LensO[*Config, *int]
func FromNillable ¶
func FromNillable[S, A any](sa Lens[S, *A]) LensO[S, *A]
FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional if the optional value is set then the `nil` value will be set instead
func FromNillableRef ¶
func FromNillableRef[S, A any](sa Lens[*S, *A]) LensO[*S, *A]
FromNillableRef returns a `Lens` for a property accessibly as a getter and setter that can be optional if the optional value is set then the `nil` value will be set instead
type Operator ¶
Operator represents a function that transforms one optional lens into another. It takes a LensO[S, A] and produces a LensO[S, B].