Documentation
¶
Overview ¶
Package option provides isomorphisms for working with Option types.
Overview ¶
This package offers utilities to convert between regular values and Option-wrapped values, particularly useful for handling zero values and optional data. It provides isomorphisms that treat certain values (like zero values) as representing absence, mapping them to None, while other values map to Some.
Core Functionality ¶
The main function in this package is FromZero, which creates an isomorphism between a comparable type T and Option[T], treating the zero value as None.
FromZero Isomorphism ¶
FromZero creates a bidirectional transformation where:
- Forward (Get): T → Option[T]
- Zero value → None
- Non-zero value → Some(value)
- Reverse (ReverseGet): Option[T] → T
- None → Zero value
- Some(value) → value
Basic Usage ¶
Working with integers:
import ( "github.com/IBM/fp-go/v2/optics/iso/option" O "github.com/IBM/fp-go/v2/option" ) isoInt := option.FromZero[int]() // Convert zero to None opt := isoInt.Get(0) // None[int] // Convert non-zero to Some opt = isoInt.Get(42) // Some(42) // Convert None to zero val := isoInt.ReverseGet(O.None[int]()) // 0 // Convert Some to value val = isoInt.ReverseGet(O.Some(42)) // 42
Use Cases ¶
## Database Nullable Columns
Convert between database NULL and Go zero values:
type User struct {
ID int
Name string
Age *int // NULL in database
Email *string
}
ageIso := option.FromZero[*int]()
// Reading from database
var dbAge *int = nil
optAge := ageIso.Get(dbAge) // None[*int]
// Writing to database
userAge := 25
dbAge = ageIso.ReverseGet(O.Some(&userAge)) // &25
## Configuration with Defaults
Handle optional configuration values:
type Config struct {
Port int
Timeout int
MaxConn int
}
portIso := option.FromZero[int]()
// Use zero as "not configured"
config := Config{Port: 0, Timeout: 30, MaxConn: 100}
portOpt := portIso.Get(config.Port) // None[int] (use default)
// Set explicit value
config.Port = portIso.ReverseGet(O.Some(8080)) // 8080
## API Response Handling
Work with APIs that use zero values to indicate absence:
type APIResponse struct {
UserID int // 0 means not set
Score float64 // 0.0 means not available
Message string // "" means no message
}
userIDIso := option.FromZero[int]()
scoreIso := option.FromZero[float64]()
messageIso := option.FromZero[string]()
response := APIResponse{UserID: 0, Score: 0.0, Message: ""}
userID := userIDIso.Get(response.UserID) // None[int]
score := scoreIso.Get(response.Score) // None[float64]
message := messageIso.Get(response.Message) // None[string]
## Validation Logic
Simplify required vs optional field validation:
import S "github.com/IBM/fp-go/v2/string"
type FormData struct {
Name string // Required
Email string // Required
Phone string // Optional (empty = not provided)
Comments string // Optional
}
phoneIso := option.FromZero[string]()
commentsIso := option.FromZero[string]()
form := FormData{
Name: "Alice",
Email: "alice@example.com",
Phone: "",
Comments: "",
}
// Check optional fields
phone := phoneIso.Get(form.Phone) // None[string]
comments := commentsIso.Get(form.Comments) // None[string]
// Validate: required fields must be non-empty
if S.IsEmpty(form.Name) || S.IsEmpty(form.Email) {
// Validation error
}
Working with Different Types ¶
## Strings
strIso := option.FromZero[string]()
opt := strIso.Get("") // None[string]
opt = strIso.Get("hello") // Some("hello")
val := strIso.ReverseGet(O.None[string]()) // ""
val = strIso.ReverseGet(O.Some("world")) // "world"
## Pointers
ptrIso := option.FromZero[*int]() opt := ptrIso.Get(nil) // None[*int] num := 42 opt = ptrIso.Get(&num) // Some(&num) val := ptrIso.ReverseGet(O.None[*int]()) // nil val = ptrIso.ReverseGet(O.Some(&num)) // &num
## Floating Point Numbers
floatIso := option.FromZero[float64]() opt := floatIso.Get(0.0) // None[float64] opt = floatIso.Get(3.14) // Some(3.14) val := floatIso.ReverseGet(O.None[float64]()) // 0.0 val = floatIso.ReverseGet(O.Some(2.71)) // 2.71
## Booleans
boolIso := option.FromZero[bool]() opt := boolIso.Get(false) // None[bool] opt = boolIso.Get(true) // Some(true) val := boolIso.ReverseGet(O.None[bool]()) // false val = boolIso.ReverseGet(O.Some(true)) // true
Composition with Other Optics ¶
Combine with lenses for nested structures:
import (
L "github.com/IBM/fp-go/v2/optics/lens"
I "github.com/IBM/fp-go/v2/optics/iso"
)
type Settings struct {
Volume int // 0 means muted
}
volumeLens := L.MakeLens(
func(s Settings) int { return s.Volume },
func(s Settings, v int) Settings {
s.Volume = v
return s
},
)
volumeIso := option.FromZero[int]()
// Compose lens with iso
volumeOptLens := F.Pipe1(
volumeLens,
L.IMap[Settings](volumeIso.Get, volumeIso.ReverseGet),
)
settings := Settings{Volume: 0}
vol := volumeOptLens.Get(settings) // None[int] (muted)
// Set volume
updated := volumeOptLens.Set(O.Some(75))(settings)
// updated.Volume == 75
Isomorphism Laws ¶
FromZero satisfies the isomorphism round-trip laws:
1. **ReverseGet(Get(t)) == t** for all t: T
isoInt := option.FromZero[int]() value := 42 result := isoInt.ReverseGet(isoInt.Get(value)) // result == 42
2. **Get(ReverseGet(opt)) == opt** for all opt: Option[T]
isoInt := option.FromZero[int]() opt := O.Some(42) result := isoInt.Get(isoInt.ReverseGet(opt)) // result == Some(42)
These laws ensure that the transformation is truly reversible with no information loss.
Performance Considerations ¶
The FromZero isomorphism is very efficient:
- No allocations for the iso structure itself
- Simple equality comparison for zero check
- Direct value unwrapping for ReverseGet
- No reflection or runtime type assertions
Type Safety ¶
The isomorphism is fully type-safe:
- Compile-time type checking ensures T is comparable
- Generic type parameters prevent type mismatches
- No runtime type assertions needed
- The compiler enforces correct usage
Limitations ¶
The FromZero isomorphism has some limitations to be aware of:
1. **Zero Value Ambiguity**: Cannot distinguish between "intentionally zero" and "absent"
- For int: 0 always maps to None, even if 0 is a valid value
- For string: "" always maps to None, even if empty string is valid
- Solution: Use a different representation (e.g., pointers) if zero is meaningful
2. **Comparable Constraint**: Only works with comparable types
- Cannot use with slices, maps, or functions
- Cannot use with structs containing non-comparable fields
- Solution: Use pointers to such types, or custom isomorphisms
3. **Boolean Limitation**: false always maps to None
- Cannot represent "explicitly false" vs "not set"
- Solution: Use *bool or a custom type if this distinction matters
Related Packages ¶
- github.com/IBM/fp-go/v2/optics/iso: Core isomorphism functionality
- github.com/IBM/fp-go/v2/option: Option type and operations
- github.com/IBM/fp-go/v2/optics/lens: Lenses for focused access
- github.com/IBM/fp-go/v2/optics/lens/option: Lenses for optional values
See Also ¶
For more information on isomorphisms and optics:
- optics/iso package documentation
- optics package overview
- option package documentation
Package option provides isomorphisms for working with Option types. It offers utilities to convert between regular values and Option-wrapped values, particularly useful for handling zero values and optional data.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FromZero ¶
func FromZero[T comparable]() iso.Iso[T, option.Option[T]]
FromZero creates an isomorphism between a comparable type T and Option[T]. The isomorphism treats the zero value of T as None and non-zero values as Some.
This is particularly useful for types where the zero value has special meaning (e.g., 0 for numbers, "" for strings, nil for pointers) and you want to represent the absence of a meaningful value using Option.
Type Parameters:
- T: A comparable type (must support == and != operators)
Returns:
- An Iso[T, Option[T]] where:
- Get: Converts T to Option[T] (zero value → None, non-zero → Some)
- ReverseGet: Converts Option[T] to T (None → zero value, Some → unwrapped value)
Behavior:
- Get direction: If the value equals the zero value of T, returns None; otherwise returns Some(value)
- ReverseGet direction: If the Option is None, returns the zero value; otherwise returns the unwrapped value
Example with integers:
isoInt := FromZero[int]() opt := isoInt.Get(0) // None (0 is the zero value) opt = isoInt.Get(42) // Some(42) val := isoInt.ReverseGet(option.None[int]()) // 0 val = isoInt.ReverseGet(option.Some(42)) // 42
Example with strings:
isoStr := FromZero[string]()
opt := isoStr.Get("") // None ("" is the zero value)
opt = isoStr.Get("hello") // Some("hello")
val := isoStr.ReverseGet(option.None[string]()) // ""
val = isoStr.ReverseGet(option.Some("world")) // "world"
Example with pointers:
isoPtr := FromZero[*int]() opt := isoPtr.Get(nil) // None (nil is the zero value) num := 42 opt = isoPtr.Get(&num) // Some(&num)
Use cases:
- Converting between database nullable columns and Go types
- Handling optional configuration values with defaults
- Working with APIs that use zero values to indicate absence
- Simplifying validation logic for required vs optional fields
Note: This isomorphism satisfies the round-trip laws:
- ReverseGet(Get(t)) == t for all t: T
- Get(ReverseGet(opt)) == opt for all opt: Option[T]
Types ¶
This section is empty.