Documentation
¶
Overview ¶
Package prism provides prisms - optics for focusing on variants within sum types.
Overview ¶
A Prism is an optic used to select a specific variant from a sum type (also known as tagged unions, discriminated unions, or algebraic data types). Unlike lenses which focus on fields that always exist, prisms focus on values that may or may not be present depending on which variant is active.
Prisms are essential for working with:
- Either types (Left/Right)
- Option types (Some/None)
- Result types (Success/Failure)
- Custom sum types
- Error handling patterns
Mathematical Foundation ¶
A Prism[S, A] consists of two operations:
- GetOption: S → Option[A] (try to extract A from S, may fail)
- ReverseGet: A → S (construct S from A, always succeeds)
Prisms must satisfy the prism laws:
- GetOptionReverseGet: prism.GetOption(prism.ReverseGet(a)) == Some(a)
- ReverseGetGetOption: if GetOption(s) == Some(a), then ReverseGet(a) produces equivalent s
These laws ensure that:
- Constructing and then extracting always succeeds
- Extracting and then constructing preserves the value
Basic Usage ¶
Creating a prism for an Either type:
type Result interface{ isResult() }
type Success struct{ Value int }
type Failure struct{ Error string }
func (Success) isResult() {}
func (Failure) isResult() {}
successPrism := prism.MakePrism(
func(r Result) option.Option[int] {
if s, ok := r.(Success); ok {
return option.Some(s.Value)
}
return option.None[int]()
},
func(v int) Result {
return Success{Value: v}
},
)
// Try to extract value from Success
result := Success{Value: 42}
value := successPrism.GetOption(result) // Some(42)
// Try to extract value from Failure
failure := Failure{Error: "oops"}
value = successPrism.GetOption(failure) // None[int]
// Construct a Success from a value
newResult := successPrism.ReverseGet(100) // Success{Value: 100}
Identity Prism ¶
The identity prism focuses on the entire value:
idPrism := prism.Id[int]() value := idPrism.GetOption(42) // Some(42) result := idPrism.ReverseGet(42) // 42
From Predicate ¶
Create a prism that matches values satisfying a predicate:
positivePrism := prism.FromPredicate(func(n int) bool {
return n > 0
})
// Matches positive numbers
value := positivePrism.GetOption(42) // Some(42)
value = positivePrism.GetOption(-5) // None[int]
// ReverseGet always succeeds (doesn't check predicate)
result := positivePrism.ReverseGet(42) // 42
Composing Prisms ¶
Prisms can be composed to focus on nested sum types:
type Outer interface{ isOuter() }
type OuterA struct{ Inner Inner }
type OuterB struct{ Value string }
type Inner interface{ isInner() }
type InnerX struct{ Data int }
type InnerY struct{ Info string }
outerAPrism := prism.MakePrism(
func(o Outer) option.Option[Inner] {
if a, ok := o.(OuterA); ok {
return option.Some(a.Inner)
}
return option.None[Inner]()
},
func(i Inner) Outer { return OuterA{Inner: i} },
)
innerXPrism := prism.MakePrism(
func(i Inner) option.Option[int] {
if x, ok := i.(InnerX); ok {
return option.Some(x.Data)
}
return option.None[int]()
},
func(d int) Inner { return InnerX{Data: d} },
)
// Compose to access InnerX data from Outer
composedPrism := F.Pipe1(
outerAPrism,
prism.Compose[Outer](innerXPrism),
)
outer := OuterA{Inner: InnerX{Data: 42}}
data := composedPrism.GetOption(outer) // Some(42)
Modifying Through Prisms ¶
Apply transformations when the variant matches:
type Status interface{ isStatus() }
type Active struct{ Count int }
type Inactive struct{}
activePrism := prism.MakePrism(
func(s Status) option.Option[int] {
if a, ok := s.(Active); ok {
return option.Some(a.Count)
}
return option.None[int]()
},
func(count int) Status { return Active{Count: count} },
)
status := Active{Count: 5}
// Increment count if active
updated := prism.Set(10)(activePrism)(status)
// Result: Active{Count: 10}
// Try to modify inactive status (no change)
inactive := Inactive{}
unchanged := prism.Set(10)(activePrism)(inactive)
// Result: Inactive{} (unchanged)
Working with Option Types ¶
The Some function creates a prism focused on the Some variant of an Option:
type Config struct {
Timeout option.Option[int]
}
timeoutPrism := prism.MakePrism(
func(c Config) option.Option[option.Option[int]] {
return option.Some(c.Timeout)
},
func(t option.Option[int]) Config {
return Config{Timeout: t}
},
)
// Focus on the Some value
somePrism := prism.Some(timeoutPrism)
config := Config{Timeout: option.Some(30)}
timeout := somePrism.GetOption(config) // Some(30)
configNone := Config{Timeout: option.None[int]()}
timeout = somePrism.GetOption(configNone) // None[int]
Bidirectional Mapping ¶
Transform the focus type of a prism:
type Message interface{ isMessage() }
type TextMessage struct{ Content string }
type ImageMessage struct{ URL string }
textPrism := prism.MakePrism(
func(m Message) option.Option[string] {
if t, ok := m.(TextMessage); ok {
return option.Some(t.Content)
}
return option.None[string]()
},
func(content string) Message {
return TextMessage{Content: content}
},
)
// Map to uppercase
upperPrism := F.Pipe1(
textPrism,
prism.IMap(
strings.ToUpper,
strings.ToLower,
),
)
msg := TextMessage{Content: "hello"}
upper := upperPrism.GetOption(msg) // Some("HELLO")
Real-World Example: Error Handling ¶
type Result[T any] interface{ isResult() }
type Success[T any] struct {
Value T
}
type Failure[T any] struct {
Error error
}
func (Success[T]) isResult() {}
func (Failure[T]) isResult() {}
func SuccessPrism[T any]() prism.Prism[Result[T], T] {
return prism.MakePrism(
func(r Result[T]) option.Option[T] {
if s, ok := r.(Success[T]); ok {
return option.Some(s.Value)
}
return option.None[T]()
},
func(v T) Result[T] {
return Success[T]{Value: v}
},
)
}
func FailurePrism[T any]() prism.Prism[Result[T], error] {
return prism.MakePrism(
func(r Result[T]) option.Option[error] {
if f, ok := r.(Failure[T]); ok {
return option.Some(f.Error)
}
return option.None[error]()
},
func(e error) Result[T] {
return Failure[T]{Error: e}
},
)
}
// Use the prisms
result := Success[int]{Value: 42}
successPrism := SuccessPrism[int]()
value := successPrism.GetOption(result) // Some(42)
failurePrism := FailurePrism[int]()
err := failurePrism.GetOption(result) // None[error]
// Transform success values
doubled := prism.Set(84)(successPrism)(result)
// Result: Success{Value: 84}
Real-World Example: JSON Parsing ¶
type JSONValue interface{ isJSON() }
type JSONString struct{ Value string }
type JSONNumber struct{ Value float64 }
type JSONBool struct{ Value bool }
type JSONNull struct{}
stringPrism := prism.MakePrism(
func(j JSONValue) option.Option[string] {
if s, ok := j.(JSONString); ok {
return option.Some(s.Value)
}
return option.None[string]()
},
func(s string) JSONValue {
return JSONString{Value: s}
},
)
numberPrism := prism.MakePrism(
func(j JSONValue) option.Option[float64] {
if n, ok := j.(JSONNumber); ok {
return option.Some(n.Value)
}
return option.None[float64]()
},
func(n float64) JSONValue {
return JSONNumber{Value: n}
},
)
// Parse and extract values
jsonValue := JSONString{Value: "hello"}
str := stringPrism.GetOption(jsonValue) // Some("hello")
num := numberPrism.GetOption(jsonValue) // None[float64]
Real-World Example: State Machine ¶
type State interface{ isState() }
type Idle struct{}
type Running struct{ Progress int }
type Completed struct{ Result string }
type Failed struct{ Error error }
runningPrism := prism.MakePrism(
func(s State) option.Option[int] {
if r, ok := s.(Running); ok {
return option.Some(r.Progress)
}
return option.None[int]()
},
func(progress int) State {
return Running{Progress: progress}
},
)
// Update progress if running
state := Running{Progress: 50}
updated := prism.Set(75)(runningPrism)(state)
// Result: Running{Progress: 75}
// Try to update when not running (no change)
idle := Idle{}
unchanged := prism.Set(75)(runningPrism)(idle)
// Result: Idle{} (unchanged)
Prisms vs Lenses ¶
While both are optics, they serve different purposes:
**Prisms:**
- Focus on variants of sum types
- GetOption may fail (returns Option)
- ReverseGet always succeeds
- Used for pattern matching
- Example: Either[Error, Value] → Value
**Lenses:**
- Focus on fields of product types
- Get always succeeds
- Set always succeeds
- Used for field access
- Example: Person → Name
Prism Laws ¶
Prisms must satisfy two laws:
**Law 1: GetOptionReverseGet**
prism.GetOption(prism.ReverseGet(a)) == Some(a)
Constructing a value and then extracting it always succeeds.
**Law 2: ReverseGetGetOption**
if prism.GetOption(s) == Some(a) then prism.ReverseGet(a) should produce equivalent s
If extraction succeeds, reconstructing should produce an equivalent value.
Performance Considerations ¶
Prisms are efficient:
- No reflection - uses type assertions
- Minimal allocations
- Composition creates function closures
- GetOption short-circuits on mismatch
For best performance:
- Cache composed prisms
- Use type switches for multiple prisms
- Consider batch operations when possible
Type Safety ¶
Prisms are fully type-safe:
- Compile-time type checking
- Type assertions are explicit
- Generic type parameters ensure correctness
- Composition maintains type relationships
Built-in Prisms ¶
The package provides many useful prisms for common transformations:
**Type Conversion & Parsing:**
- FromEncoding(enc): Base64 encoding/decoding - Prism[string, []byte]
- ParseURL(): URL parsing/formatting - Prism[string, *url.URL]
- ParseDate(layout): Date parsing with custom layouts - Prism[string, time.Time]
- ParseInt(): Integer string parsing - Prism[string, int]
- ParseInt64(): 64-bit integer parsing - Prism[string, int64]
- ParseBool(): Boolean string parsing - Prism[string, bool]
- ParseFloat32(): 32-bit float parsing - Prism[string, float32]
- ParseFloat64(): 64-bit float parsing - Prism[string, float64]
**Type Assertion & Extraction:**
- InstanceOf[T](): Safe type assertion from any - Prism[any, T]
- Deref[T](): Safe pointer dereferencing (filters nil) - Prism[*T, *T]
**Container/Wrapper Prisms:**
- FromEither[E, T](): Extract Right values - Prism[Either[E, T], T] ReverseGet wraps into Right (acts as success constructor)
- FromResult[T](): Extract success from Result - Prism[Result[T], T] ReverseGet wraps into success Result
- FromOption[T](): Extract Some values - Prism[Option[T], T] ReverseGet wraps into Some (acts as Some constructor)
**Validation Prisms:**
- FromZero[T](): Match only zero/default values - Prism[T, T]
- FromNonZero[T](): Match only non-zero values - Prism[T, T]
**Pattern Matching:**
- RegexMatcher(re): Extract regex matches with groups - Prism[string, Match]
- RegexNamedMatcher(re): Extract named regex groups - Prism[string, NamedMatch]
Example using built-in prisms:
// Parse and validate an integer from a string
intPrism := prism.ParseInt()
value := intPrism.GetOption("42") // Some(42)
invalid := intPrism.GetOption("abc") // None[int]()
// Extract success values from Either
resultPrism := prism.FromEither[error, int]()
success := either.Right[error](100)
value = resultPrism.GetOption(success) // Some(100)
// ReverseGet acts as a constructor
wrapped := resultPrism.ReverseGet(42) // Right(42)
// Compose prisms for complex transformations
// Parse string to int, then wrap in Option
composed := F.Pipe1(
prism.ParseInt(),
prism.Compose[string](prism.FromOption[int]()),
)
Function Reference ¶
Core Functions:
- MakePrism: Create a prism from GetOption and ReverseGet functions
- Id: Create an identity prism
- FromPredicate: Create a prism from a predicate function
- Compose: Compose two prisms
Transformation:
- Set: Set a value through a prism (no-op if variant doesn't match)
- IMap: Bidirectionally map a prism
Specialized:
- Some: Focus on the Some variant of an Option
Related Packages ¶
- github.com/IBM/fp-go/v2/optics/lens: Lenses for product types
- github.com/IBM/fp-go/v2/optics/iso: Isomorphisms
- github.com/IBM/fp-go/v2/optics/optional: Optional optics
- github.com/IBM/fp-go/v2/option: Optional values
- github.com/IBM/fp-go/v2/either: Sum types
- github.com/IBM/fp-go/v2/function: Function composition
Index ¶
- func AsTraversal[R ~func(func(A) HKTA) func(S) HKTS, S, A, HKTS, HKTA any](fof func(S) HKTS, fmap func(HKTA, func(A) S) HKTS) func(Prism[S, A]) R
- func Set[S, A any](a A) func(Prism[S, A]) Endomorphism[S]
- type Either
- type Endomorphism
- type Kleisli
- type Match
- type NamedMatch
- type Operator
- type Option
- type Prism
- func Deref[T any]() Prism[*T, *T]
- func FromEither[E, T any]() Prism[Either[E, T], T]
- func FromEncoding(enc *base64.Encoding) Prism[string, []byte]
- func FromNonZero[T comparable]() Prism[T, T]
- func FromOption[T any]() Prism[Option[T], T]
- func FromPredicate[S any](pred func(S) bool) Prism[S, S]
- func FromResult[T any]() Prism[Result[T], T]
- func FromZero[T comparable]() Prism[T, T]
- func Id[S any]() Prism[S, S]
- func InstanceOf[T any]() Prism[any, T]
- func MakePrism[S, A any](get O.Kleisli[S, A], rev func(A) S) Prism[S, A]
- func MakePrismWithName[S, A any](get O.Kleisli[S, A], rev func(A) S, name string) Prism[S, A]
- func ParseBool() Prism[string, bool]
- func ParseDate(layout string) Prism[string, time.Time]
- func ParseFloat32() Prism[string, float32]
- func ParseFloat64() Prism[string, float64]
- func ParseInt() Prism[string, int]
- func ParseInt64() Prism[string, int64]
- func ParseURL() Prism[string, *url.URL]
- func RegexMatcher(re *regexp.Regexp) Prism[string, Match]
- func RegexNamedMatcher(re *regexp.Regexp) Prism[string, NamedMatch]
- func Some[S, A any](soa Prism[S, Option[A]]) Prism[S, A]
- type Reader
- type Result
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AsTraversal ¶
func AsTraversal[R ~func(func(A) HKTA) func(S) HKTS, S, A, HKTS, HKTA any]( fof func(S) HKTS, fmap func(HKTA, func(A) S) HKTS, ) func(Prism[S, A]) R
AsTraversal 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 Set ¶
Set 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 := Set[Option[int], int](100) result := setter(somePrism)(Some(42)) // Some(100) result = setter(somePrism)(None[int]()) // None[int]() (unchanged)
Types ¶
type Either ¶
Either is a type alias for either.Either[E, T], representing a value that can be one of two types. It is re-exported here for convenience when working with prisms that handle error cases.
An Either[E, T] can be either:
- Left(error): Contains an error value of type E
- Right(value): Contains a success value of type T
This type is commonly used in prism operations for error handling, particularly with the FromEither prism which extracts Right values and returns None for Left values.
Type Parameters:
- E: The type of the error/left value
- T: The type of the success/right value
Example:
// Using FromEither prism to extract success values
prism := FromEither[error, int]()
// Extract from a Right value
success := either.Right[error](42)
result := prism.GetOption(success) // Returns Some(42)
// Extract from a Left value
failure := either.Left[int](errors.New("failed"))
result = prism.GetOption(failure) // Returns None
// ReverseGet wraps a value into Right
wrapped := prism.ReverseGet(100) // Returns Right(100)
Common Use Cases:
- Error handling in functional pipelines
- Representing computations that may fail
- Composing prisms that work with Either types
See also:
- github.com/IBM/fp-go/v2/either for the full Either API
- FromEither for creating prisms that work with Either types
- Prism composition for building complex error-handling pipelines
type Endomorphism ¶
type Endomorphism[T any] = endomorphism.Endomorphism[T]
Endomorphism represents a function from a type to itself (T → T).
type Kleisli ¶
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 Match ¶
type Match struct {
Before string // Text before the match
Groups []string // Capture groups (index 0 is full match)
After string // Text after the match
}
Match represents a regex match result with full reconstruction capability. It contains everything needed to reconstruct the original string, making it suitable for use in a prism that maintains bidirectionality.
Fields:
- Before: Text before the match
- Groups: Capture groups (index 0 is the full match, 1+ are capture groups)
- After: Text after the match
Example:
// For string "hello world 123" with regex `\d+`:
// Match{
// Before: "hello world ",
// Groups: []string{"123"},
// After: "",
// }
func (Match) FullMatch ¶
FullMatch returns the complete matched text (the entire regex match). This is equivalent to Groups[0] and represents what the regex matched.
Returns:
- The full matched text
Example:
match := Match{
Before: "price: ",
Groups: []string{"$99.99", "99.99"},
After: " USD",
}
full := match.FullMatch() // "$99.99"
func (Match) Group ¶
Group returns the nth capture group from the match (1-indexed). Capture group 0 is the full match, groups 1+ are the parenthesized captures. Returns an empty string if the group index is out of bounds.
Parameters:
- n: The capture group index (1-indexed)
Returns:
- The captured text, or empty string if index is invalid
Example:
// Regex: `(\w+)@(\w+\.\w+)` matching "user@example.com"
match := Match{
Groups: []string{"user@example.com", "user", "example.com"},
}
username := match.Group(1) // "user"
domain := match.Group(2) // "example.com"
invalid := match.Group(5) // ""
func (Match) Reconstruct ¶
Reconstruct builds the original string from a Match. This is the inverse operation of regex matching, allowing full round-trip conversion.
Returns:
- The original string that was matched
Example:
match := Match{
Before: "hello ",
Groups: []string{"world"},
After: "!",
}
original := match.Reconstruct() // "hello world!"
type NamedMatch ¶
type NamedMatch struct {
Before string
Groups map[string]string
Full string // The full matched text
After string
}
NamedMatch represents a regex match result with named capture groups. It provides access to captured text by name rather than by index, making regex patterns more readable and maintainable.
Fields:
- Before: Text before the match
- Groups: Map of capture group names to their matched text
- Full: The complete matched text
- After: Text after the match
Example:
// For regex `(?P<user>\w+)@(?P<domain>\w+\.\w+)` matching "user@example.com":
// NamedMatch{
// Before: "",
// Groups: map[string]string{"user": "user", "domain": "example.com"},
// Full: "user@example.com",
// After: "",
// }
func (NamedMatch) Reconstruct ¶
func (nm NamedMatch) Reconstruct() string
Reconstruct builds the original string from a NamedMatch. This is the inverse operation of regex matching, allowing full round-trip conversion.
Returns:
- The original string that was matched
Example:
match := NamedMatch{
Before: "email: ",
Full: "user@example.com",
Groups: map[string]string{"user": "user", "domain": "example.com"},
After: "",
}
original := match.Reconstruct() // "email: user@example.com"
type Operator ¶
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 Compose ¶
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 IMap ¶
IMap 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 := IMap[Result](
func(n int) string { return strconv.Itoa(n) },
func(s string) int { n, _ := strconv.Atoi(s); return n },
)(intPrism) // Prism[Result, string]
type Option ¶
Option is a type alias for O.Option[T], representing an optional value. It is re-exported here for convenience when working with prisms.
An Option[T] can be either:
- Some(value): Contains a value of type T
- None: Represents the absence of a value
This type is commonly used in prism operations, particularly in the GetOption method which returns Option[A] to indicate whether a value could be extracted from the source type.
Type Parameters:
- T: The type of the value that may or may not be present
Example:
// A prism's GetOption returns an Option
prism := MakePrism(...)
result := prism.GetOption(value) // Returns Option[A]
// Check if the value was extracted successfully
if O.IsSome(result) {
// Value was found
} else {
// Value was not found (None)
}
See also:
- github.com/IBM/fp-go/v2/option for the full Option API
- Prism.GetOption for the primary use case within this package
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 Deref ¶
Deref creates a prism for safely dereferencing pointers. It provides a safe way to work with nullable pointers, handling nil values gracefully through the Option type.
The prism's GetOption attempts to dereference a pointer. If the pointer is non-nil, it returns Some(*T); if it's nil, it returns None.
The prism's ReverseGet is the identity function, returning the pointer unchanged.
Type Parameters:
- T: The type being pointed to
Returns:
- A Prism[*T, *T] that safely handles pointer dereferencing
Example:
// Create a prism for dereferencing int pointers derefPrism := Deref[int]() // Dereference non-nil pointer value := 42 ptr := &value result := derefPrism.GetOption(ptr) // Some(&42) // Dereference nil pointer var nilPtr *int result = derefPrism.GetOption(nilPtr) // None[*int]() // ReverseGet returns the pointer unchanged reconstructed := derefPrism.ReverseGet(ptr) // &42 // Use with Set to update non-nil pointers newValue := 100 newPtr := &newValue setter := Set[*int, *int](newPtr) result := setter(derefPrism)(ptr) // &100 result = setter(derefPrism)(nilPtr) // nil (unchanged)
Common use cases:
- Safely working with optional pointer fields
- Validating non-nil pointers before operations
- Filtering out nil values in data pipelines
- Working with database nullable columns
func FromEither ¶
FromEither creates a prism for extracting Right values from Either types. It provides a safe way to work with Either values, focusing on the success case and handling the error case gracefully through the Option type.
The prism's GetOption attempts to extract the Right value from an Either. If the Either is Right(value), it returns Some(value); if it's Left(error), it returns None.
The prism's ReverseGet always succeeds, wrapping a value into a Right.
Type Parameters:
- E: The error/left type
- T: The value/right type
Returns:
- A Prism[Either[E, T], T] that safely extracts Right values
Example:
// Create a prism for extracting successful results
resultPrism := FromEither[error, int]()
// Extract from Right
success := either.Right[error](42)
result := resultPrism.GetOption(success) // Some(42)
// Extract from Left
failure := either.Left[int](errors.New("failed"))
result = resultPrism.GetOption(failure) // None[int]()
// Wrap value into Right
wrapped := resultPrism.ReverseGet(100) // Right(100)
// Use with Set to update successful results
setter := Set[Either[error, int], int](200)
result := setter(resultPrism)(success) // Right(200)
result = setter(resultPrism)(failure) // Left(error) (unchanged)
Common use cases:
- Extracting successful values from Either results
- Filtering out errors in data pipelines
- Working with fallible operations
- Composing with other prisms for complex error handling
func FromEncoding ¶
FromEncoding creates a prism for base64 encoding/decoding operations. It provides a safe way to work with base64-encoded strings, handling encoding and decoding errors gracefully through the Option type.
The prism's GetOption attempts to decode a base64 string into bytes. If decoding succeeds, it returns Some([]byte); if it fails (e.g., invalid base64 format), it returns None.
The prism's ReverseGet always succeeds, encoding bytes into a base64 string.
Parameters:
- enc: A base64.Encoding instance (e.g., base64.StdEncoding, base64.URLEncoding)
Returns:
- A Prism[string, []byte] that safely handles base64 encoding/decoding
Example:
// Create a prism for standard base64 encoding
b64Prism := FromEncoding(base64.StdEncoding)
// Decode valid base64 string
data := b64Prism.GetOption("SGVsbG8gV29ybGQ=") // Some([]byte("Hello World"))
// Decode invalid base64 string
invalid := b64Prism.GetOption("not-valid-base64!!!") // None[[]byte]()
// Encode bytes to base64
encoded := b64Prism.ReverseGet([]byte("Hello World")) // "SGVsbG8gV29ybGQ="
// Use with Set to update encoded values
newData := []byte("New Data")
setter := Set[string, []byte](newData)
result := setter(b64Prism)("SGVsbG8gV29ybGQ=") // Encodes newData to base64
Common use cases:
- Safely decoding base64-encoded configuration values
- Working with base64-encoded API responses
- Validating and transforming base64 data in pipelines
- Using different encodings (Standard, URL-safe, RawStd, RawURL)
func FromNonZero ¶
func FromNonZero[T comparable]() Prism[T, T]
FromNonZero creates a prism that matches non-zero values of comparable types. It provides a safe way to work with non-zero values, handling zero values gracefully through the Option type.
The prism's GetOption returns Some(t) if the value is not equal to the zero value of type T; otherwise, it returns None.
The prism's ReverseGet is the identity function, returning the value unchanged.
Type Parameters:
- T: A comparable type (must support == and != operators)
Returns:
- A Prism[T, T] that matches non-zero values
Example:
// Create a prism for non-zero integers nonZeroPrism := FromNonZero[int]() // Match non-zero value result := nonZeroPrism.GetOption(42) // Some(42) // Zero returns None result = nonZeroPrism.GetOption(0) // None[int]() // ReverseGet is identity value := nonZeroPrism.ReverseGet(42) // 42 // Use with Set to update non-zero values setter := Set[int, int](100) result := setter(nonZeroPrism)(42) // 100 result = setter(nonZeroPrism)(0) // 0 (unchanged)
Common use cases:
- Validating that values are non-zero/non-default
- Filtering non-zero values in data pipelines
- Working with required fields that shouldn't be zero
- Replacing non-zero values with new values
func FromOption ¶
FromOption 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 FromPredicate ¶
FromPredicate 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 := FromPredicate(N.MoreThan(0)) value := positivePrism.GetOption(42) // Some(42) value = positivePrism.GetOption(-5) // None[int]
func FromResult ¶
func FromZero ¶
func FromZero[T comparable]() Prism[T, T]
FromZero creates a prism that matches zero values of comparable types. It provides a safe way to work with zero values, handling non-zero values gracefully through the Option type.
The prism's GetOption returns Some(t) if the value equals the zero value of type T; otherwise, it returns None.
The prism's ReverseGet is the identity function, returning the value unchanged.
Type Parameters:
- T: A comparable type (must support == and != operators)
Returns:
- A Prism[T, T] that matches zero values
Example:
// Create a prism for zero integers zeroPrism := FromZero[int]() // Match zero value result := zeroPrism.GetOption(0) // Some(0) // Non-zero returns None result = zeroPrism.GetOption(42) // None[int]() // ReverseGet is identity value := zeroPrism.ReverseGet(0) // 0 // Use with Set to update zero values setter := Set[int, int](100) result := setter(zeroPrism)(0) // 100 result = setter(zeroPrism)(42) // 42 (unchanged)
Common use cases:
- Validating that values are zero/default
- Filtering zero values in data pipelines
- Working with optional fields that use zero as "not set"
- Replacing zero values with defaults
func Id ¶
Id 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 := Id[int]() value := idPrism.GetOption(42) // Some(42) result := idPrism.ReverseGet(42) // 42
func InstanceOf ¶
InstanceOf creates a prism for type assertions on interface{}/any values. It provides a safe way to extract values of a specific type from an any value, handling type mismatches gracefully through the Option type.
The prism's GetOption attempts to assert that an any value is of type T. If the assertion succeeds, it returns Some(T); if it fails, it returns None.
The prism's ReverseGet always succeeds, converting a value of type T back to any.
Type Parameters:
- T: The target type to extract from any
Returns:
- A Prism[any, T] that safely handles type assertions
Example:
// Create a prism for extracting int values intPrism := InstanceOf[int]() // Extract int from any var value any = 42 result := intPrism.GetOption(value) // Some(42) // Type mismatch returns None var strValue any = "hello" result = intPrism.GetOption(strValue) // None[int]() // Convert back to any anyValue := intPrism.ReverseGet(42) // any(42) // Use with Set to update typed values setter := Set[any, int](100) result := setter(intPrism)(any(42)) // any(100)
Common use cases:
- Safely extracting typed values from interface{} collections
- Working with heterogeneous data structures
- Type-safe deserialization and validation
- Pattern matching on interface{} values
func MakePrism ¶
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 ParseBool ¶
ParseBool creates a prism for parsing and formatting boolean values. It provides a safe way to convert between string and bool, handling parsing errors gracefully through the Option type.
The prism's GetOption attempts to parse a string into a bool. It accepts "1", "t", "T", "TRUE", "true", "True", "0", "f", "F", "FALSE", "false", "False". If parsing succeeds, it returns Some(bool); if it fails, it returns None.
The prism's ReverseGet always succeeds, converting a bool to "true" or "false".
Returns:
- A Prism[string, bool] that safely handles bool parsing/formatting
Example:
// Create a bool parsing prism
boolPrism := ParseBool()
// Parse valid boolean strings
parsed := boolPrism.GetOption("true") // Some(true)
parsed = boolPrism.GetOption("1") // Some(true)
parsed = boolPrism.GetOption("false") // Some(false)
parsed = boolPrism.GetOption("0") // Some(false)
// Parse invalid boolean
invalid := boolPrism.GetOption("maybe") // None[bool]()
// Format bool to string
str := boolPrism.ReverseGet(true) // "true"
str = boolPrism.ReverseGet(false) // "false"
// Use with Set to update boolean values
setter := Set[string, bool](true)
result := setter(boolPrism)("false") // "true"
Common use cases:
- Parsing boolean configuration values
- Validating boolean user input
- Converting between string and bool in data pipelines
- Working with boolean API parameters or flags
func ParseDate ¶
ParseDate creates a prism for parsing and formatting dates with a specific layout. It provides a safe way to work with date strings, handling parsing errors gracefully through the Option type.
The prism's GetOption attempts to parse a string into a time.Time using the specified layout. If parsing succeeds, it returns Some(time.Time); if it fails (e.g., invalid date format), it returns None.
The prism's ReverseGet always succeeds, formatting a time.Time back to a string using the same layout.
Parameters:
- layout: The time layout string (e.g., "2006-01-02", time.RFC3339)
Returns:
- A Prism[string, time.Time] that safely handles date parsing/formatting
Example:
// Create a prism for ISO date format
datePrism := ParseDate("2006-01-02")
// Parse valid date
parsed := datePrism.GetOption("2024-03-15")
// Some(time.Time{2024, 3, 15, ...})
// Parse invalid date
invalid := datePrism.GetOption("not-a-date") // None[time.Time]()
// Format date back to string
date := time.Date(2024, 3, 15, 0, 0, 0, 0, time.UTC)
str := datePrism.ReverseGet(date) // "2024-03-15"
// Use with Set to update dates
newDate := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
setter := Set[string, time.Time](newDate)
result := setter(datePrism)("2024-03-15") // "2025-01-01"
// Different layouts for different formats
rfc3339Prism := ParseDate(time.RFC3339)
parsed = rfc3339Prism.GetOption("2024-03-15T10:30:00Z")
Common use cases:
- Validating and parsing date configuration values
- Working with date strings in APIs
- Converting between date formats
- Safely handling user-provided date inputs
func ParseFloat32 ¶
ParseFloat32 creates a prism for parsing and formatting 32-bit floating-point numbers. It provides a safe way to convert between string and float32, handling parsing errors gracefully through the Option type.
The prism's GetOption attempts to parse a string into a float32. If parsing succeeds, it returns Some(float32); if it fails (e.g., invalid number format or overflow), it returns None.
The prism's ReverseGet always succeeds, converting a float32 to its string representation using the 'g' format (shortest representation).
Returns:
- A Prism[string, float32] that safely handles float32 parsing/formatting
Example:
// Create a float32 parsing prism
float32Prism := ParseFloat32()
// Parse valid float
parsed := float32Prism.GetOption("3.14") // Some(3.14)
parsed = float32Prism.GetOption("1.5e10") // Some(1.5e10)
// Parse invalid float
invalid := float32Prism.GetOption("not-a-number") // None[float32]()
// Format float32 to string
str := float32Prism.ReverseGet(float32(3.14)) // "3.14"
// Use with Set to update float32 values
setter := Set[string, float32](float32(2.71))
result := setter(float32Prism)("3.14") // "2.71"
Common use cases:
- Parsing floating-point configuration values
- Working with scientific notation
- Converting between string and float32 in data pipelines
- Handling numeric API parameters with decimal precision
func ParseFloat64 ¶
ParseFloat64 creates a prism for parsing and formatting 64-bit floating-point numbers. It provides a safe way to convert between string and float64, handling parsing errors gracefully through the Option type.
The prism's GetOption attempts to parse a string into a float64. If parsing succeeds, it returns Some(float64); if it fails (e.g., invalid number format or overflow), it returns None.
The prism's ReverseGet always succeeds, converting a float64 to its string representation using the 'g' format (shortest representation).
Returns:
- A Prism[string, float64] that safely handles float64 parsing/formatting
Example:
// Create a float64 parsing prism
float64Prism := ParseFloat64()
// Parse valid float
parsed := float64Prism.GetOption("3.141592653589793") // Some(3.141592653589793)
parsed = float64Prism.GetOption("1.5e100") // Some(1.5e100)
// Parse invalid float
invalid := float64Prism.GetOption("not-a-number") // None[float64]()
// Format float64 to string
str := float64Prism.ReverseGet(3.141592653589793) // "3.141592653589793"
// Use with Set to update float64 values
setter := Set[string, float64](2.718281828459045)
result := setter(float64Prism)("3.14") // "2.718281828459045"
Common use cases:
- Parsing high-precision floating-point values
- Working with scientific notation and large numbers
- Converting between string and float64 in data pipelines
- Handling precise numeric API parameters
func ParseInt ¶
ParseInt creates a prism for parsing and formatting integers. It provides a safe way to convert between string and int, handling parsing errors gracefully through the Option type.
The prism's GetOption attempts to parse a string into an int. If parsing succeeds, it returns Some(int); if it fails (e.g., invalid number format), it returns None.
The prism's ReverseGet always succeeds, converting an int to its string representation.
Returns:
- A Prism[string, int] that safely handles int parsing/formatting
Example:
// Create an int parsing prism
intPrism := ParseInt()
// Parse valid integer
parsed := intPrism.GetOption("42") // Some(42)
// Parse invalid integer
invalid := intPrism.GetOption("not-a-number") // None[int]()
// Format int to string
str := intPrism.ReverseGet(42) // "42"
// Use with Set to update integer values
setter := Set[string, int](100)
result := setter(intPrism)("42") // "100"
Common use cases:
- Parsing integer configuration values
- Validating numeric user input
- Converting between string and int in data pipelines
- Working with numeric API parameters
func ParseInt64 ¶
ParseInt64 creates a prism for parsing and formatting 64-bit integers. It provides a safe way to convert between string and int64, handling parsing errors gracefully through the Option type.
The prism's GetOption attempts to parse a string into an int64. If parsing succeeds, it returns Some(int64); if it fails (e.g., invalid number format or overflow), it returns None.
The prism's ReverseGet always succeeds, converting an int64 to its string representation.
Returns:
- A Prism[string, int64] that safely handles int64 parsing/formatting
Example:
// Create an int64 parsing prism
int64Prism := ParseInt64()
// Parse valid 64-bit integer
parsed := int64Prism.GetOption("9223372036854775807") // Some(9223372036854775807)
// Parse invalid integer
invalid := int64Prism.GetOption("not-a-number") // None[int64]()
// Format int64 to string
str := int64Prism.ReverseGet(int64(42)) // "42"
// Use with Set to update int64 values
setter := Set[string, int64](int64(100))
result := setter(int64Prism)("42") // "100"
Common use cases:
- Parsing large integer values (timestamps, IDs)
- Working with database integer columns
- Handling 64-bit numeric API parameters
- Converting between string and int64 in data pipelines
func ParseURL ¶
ParseURL creates a prism for parsing and formatting URLs. It provides a safe way to work with URL strings, handling parsing errors gracefully through the Option type.
The prism's GetOption attempts to parse a string into a *url.URL. If parsing succeeds, it returns Some(*url.URL); if it fails (e.g., invalid URL format), it returns None.
The prism's ReverseGet always succeeds, converting a *url.URL back to its string representation.
Returns:
- A Prism[string, *url.URL] that safely handles URL parsing/formatting
Example:
// Create a URL parsing prism
urlPrism := ParseURL()
// Parse valid URL
parsed := urlPrism.GetOption("https://example.com/path?query=value")
// Some(*url.URL{Scheme: "https", Host: "example.com", ...})
// Parse invalid URL
invalid := urlPrism.GetOption("ht!tp://invalid url") // None[*url.URL]()
// Convert URL back to string
u, _ := url.Parse("https://example.com")
str := urlPrism.ReverseGet(u) // "https://example.com"
// Use with Set to update URLs
newURL, _ := url.Parse("https://newsite.com")
setter := Set[string, *url.URL](newURL)
result := setter(urlPrism)("https://oldsite.com") // "https://newsite.com"
Common use cases:
- Validating and parsing URL configuration values
- Working with API endpoints
- Transforming URL strings in data pipelines
- Extracting and modifying URL components safely
func RegexMatcher ¶
RegexMatcher creates a prism for regex pattern matching with full reconstruction. It provides a safe way to match strings against a regex pattern, extracting match information while maintaining the ability to reconstruct the original string.
The prism's GetOption attempts to match the regex against the string. If a match is found, it returns Some(Match) with all capture groups and context; if no match is found, it returns None.
The prism's ReverseGet reconstructs the original string from a Match.
Parameters:
- re: A compiled regular expression
Returns:
- A Prism[string, Match] that safely handles regex matching
Example:
// Create a prism for matching numbers
numRegex := regexp.MustCompile(`\d+`)
numPrism := RegexMatcher(numRegex)
// Match a string
match := numPrism.GetOption("price: 42 dollars")
// Some(Match{Before: "price: ", Groups: ["42"], After: " dollars"})
// No match returns None
noMatch := numPrism.GetOption("no numbers here") // None[Match]()
// Reconstruct original string
if m, ok := option.IsSome(match); ok {
original := numPrism.ReverseGet(m) // "price: 42 dollars"
}
// Extract capture groups
emailRegex := regexp.MustCompile(`(\w+)@(\w+\.\w+)`)
emailPrism := RegexMatcher(emailRegex)
match = emailPrism.GetOption("contact: user@example.com")
// Match.Group(1) = "user", Match.Group(2) = "example.com"
Common use cases:
- Parsing structured text with regex patterns
- Extracting and validating data from strings
- Text transformation pipelines
- Pattern-based string manipulation with reconstruction
Note: This prism is bijective - you can always reconstruct the original string from a Match, making it suitable for round-trip transformations.
func RegexNamedMatcher ¶
func RegexNamedMatcher(re *regexp.Regexp) Prism[string, NamedMatch]
RegexNamedMatcher creates a prism for regex pattern matching with named capture groups. It provides a safe way to match strings against a regex pattern with named groups, making it easier to extract specific parts of the match by name rather than index.
The prism's GetOption attempts to match the regex against the string. If a match is found, it returns Some(NamedMatch) with all named capture groups; if no match is found, it returns None.
The prism's ReverseGet reconstructs the original string from a NamedMatch.
Parameters:
- re: A compiled regular expression with named capture groups
Returns:
- A Prism[string, NamedMatch] that safely handles regex matching with named groups
Example:
// Create a prism for matching email addresses with named groups
emailRegex := regexp.MustCompile(`(?P<user>\w+)@(?P<domain>\w+\.\w+)`)
emailPrism := RegexNamedMatcher(emailRegex)
// Match a string
match := emailPrism.GetOption("contact: user@example.com")
// Some(NamedMatch{
// Before: "contact: ",
// Groups: {"user": "user", "domain": "example.com"},
// Full: "user@example.com",
// After: "",
// })
// Access named groups
if m, ok := option.IsSome(match); ok {
username := m.Groups["user"] // "user"
domain := m.Groups["domain"] // "example.com"
}
// No match returns None
noMatch := emailPrism.GetOption("invalid-email") // None[NamedMatch]()
// Reconstruct original string
if m, ok := option.IsSome(match); ok {
original := emailPrism.ReverseGet(m) // "contact: user@example.com"
}
// More complex example with date parsing
dateRegex := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
datePrism := RegexNamedMatcher(dateRegex)
match = datePrism.GetOption("Date: 2024-03-15")
// Access: match.Groups["year"], match.Groups["month"], match.Groups["day"]
Common use cases:
- Parsing structured text with meaningful field names
- Extracting and validating data from formatted strings
- Log parsing with named fields
- Configuration file parsing
- URL route parameter extraction
Note: Only named capture groups appear in the Groups map. Unnamed groups are not included. The Full field always contains the complete matched text.
func Some ¶
Some creates a prism that focuses on the Some 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 Some.
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 Some
Example:
type Config struct { Timeout Option[int] }
configPrism := MakePrism(...) // Prism[Config, Option[int]]
timeoutPrism := Some(configPrism) // Prism[Config, int]
value := timeoutPrism.GetOption(Config{Timeout: Some(30)}) // Some(30)
func (Prism[S, T]) Format ¶
Format implements fmt.Formatter for Prism. Supports all standard format verbs:
- %s, %v, %+v: uses String() representation (prism name)
- %#v: uses GoString() representation
- %q: quoted String() representation
- other verbs: uses String() representation
Example:
successPrism := prism.MakePrismWithName(..., "Result.Success")
fmt.Printf("%s", successPrism) // "Result.Success"
fmt.Printf("%v", successPrism) // "Result.Success"
fmt.Printf("%#v", successPrism) // "prism.Prism[Result, int]{name: \"Result.Success\"}"
func (Prism[S, T]) GoString ¶
GoString implements fmt.GoStringer for Prism. Returns a Go-syntax representation of the Prism value.
Example:
successPrism := prism.MakePrismWithName(..., "Result.Success")
successPrism.GoString() // "prism.Prism[Result, int]{name: \"Result.Success\"}"
func (Prism[S, T]) LogValue ¶
LogValue implements slog.LogValuer for Prism. Returns a slog.Value that represents the Prism for structured logging. Logs the prism name as a string value.
Example:
logger := slog.Default()
successPrism := prism.MakePrismWithName(..., "Result.Success")
logger.Info("using prism", "prism", successPrism)
// Logs: {"msg":"using prism","prism":"Result.Success"}