Documentation
¶
Overview ¶
Package codec provides pre-built codec implementations for common types. This package includes codecs for URL parsing, date/time formatting, and other standard data transformations that require bidirectional encoding/decoding.
The codecs in this package follow functional programming principles and integrate with the validation framework to provide type-safe, composable transformations.
Package codec provides a functional approach to encoding and decoding data with validation.
The codec package combines the concepts of encoders and decoders into a unified Type that can both encode values to an output format and decode/validate values from an input format. This is particularly useful for data serialization, API validation, and type-safe transformations.
Core Concepts ¶
Type[A, O, I]: A bidirectional codec that can:
- Decode input I to type A with validation
- Encode type A to output O
- Check if a value is of type A
Validation: Decoding returns Either[Errors, A] which represents:
- Left(Errors): Validation failed with detailed error information
- Right(A): Successfully decoded and validated value
Context: A stack of ContextEntry values that tracks the path through nested structures during validation, providing detailed error messages.
Basic Usage ¶
Creating a simple type:
nilType := codec.MakeNilType[string]()
result := nilType.Decode(nil) // Success
result := nilType.Decode("not nil") // Failure
Composing types with Pipe:
composed := codec.Pipe(typeB)(typeA) // Decodes: I -> A -> B // Encodes: B -> A -> O
Type Parameters ¶
Most functions use three type parameters:
- A: The domain type (the actual Go type being encoded/decoded)
- O: The output type for encoding
- I: The input type for decoding
Validation Errors ¶
ValidationError contains:
- Value: The actual value that failed validation
- Context: The path to the value in nested structures
- Message: Human-readable error description
Integration ¶
This package integrates with:
- optics/decoder: For decoding operations
- optics/encoder: For encoding operations
- either: For validation results
- option: For optional type checking
- reader: For context-dependent operations
Index ¶
- type Codec
- type Context
- type Decode
- type Decoder
- type Encode
- type Encoder
- type Endomorphism
- type FileInfoWithPath
- type Formattable
- type Iso
- type Kleisli
- type Lazy
- type Lens
- type Monoid
- type Operator
- func Alt[A, O, I any](second Lazy[Type[A, O, I]]) Operator[A, A, O, I]
- func AltW[R, L, O, I any](leftItem Type[L, O, I]) Operator[R, either.Either[L, R], O, I]
- func ApSL[S, T, O, I any](m Monoid[O], l Lens[S, T], fa Type[T, O, I]) Operator[S, S, O, I]
- func ApSO[S, T, O, I any](m Monoid[O], o optional.Optional[S, T], fa Type[T, O, I]) Operator[S, S, O, I]
- func Bind[S, T, O, I any](m Monoid[O], l Lens[S, T], f Kleisli[S, T, O, I]) Operator[S, S, O, I]
- func EitherOf[L, R, O, I any](m Monoid[O], onLeft Type[L, O, I], onRight Type[R, O, I]) Operator[bool, either.Either[L, R], O, I]
- func Optional[A, O, I any](m Monoid[O], onSome Type[A, O, I]) Operator[bool, Option[A], O, I]
- func Pipe[O, I, A, B any](ab Type[B, A, A]) Operator[A, B, O, I]
- type Option
- type Pair
- type Prism
- type Reader
- type ReaderResult
- type Refinement
- type Result
- type Semigroup
- type Type
- func Array[T, O any](item Type[T, O, any]) Type[[]T, []O, any]
- func BindTo[S, T, O, I any](m Monoid[O], p Prism[S, T], t Type[T, O, I]) Type[S, O, I]
- func Bool() Type[bool, bool, any]
- func BoolFromString() Type[bool, string, string]
- func Date(layout string) Type[time.Time, string, string]
- func Do[I, A, O any](e Lazy[Pair[O, A]]) Type[A, O, I]
- func Empty[I, A, O any](e Lazy[Pair[O, A]]) Type[A, O, I]
- func FromIso[A, I any](iso Iso[I, A]) Type[A, I, I]
- func FromNonZero[T comparable]() Type[T, T, T]
- func FromRefinement[A, B any](refinement Refinement[A, B]) Type[B, A, A]
- func Id[T any]() Type[T, T, T]
- func Int() Type[int, int, any]
- func Int64FromString() Type[int64, string, string]
- func IntFromString() Type[int, string, string]
- func MakeSimpleType[A any]() Type[A, A, any]
- func MakeType[A, O, I any](name string, is Reader[any, Result[A]], validate Validate[I, A], ...) Type[A, O, I]
- func MarshalJSON[T any](enc json.Marshaler, dec json.Unmarshaler) Type[T, []byte, []byte]
- func MarshalText[T any](enc encoding.TextMarshaler, dec encoding.TextUnmarshaler) Type[T, []byte, []byte]
- func MonadAlt[A, O, I any](first Type[A, O, I], second Lazy[Type[A, O, I]]) Type[A, O, I]
- func Nil[A any]() Type[*A, *A, any]
- func NonEmptyString() Type[string, string, string]
- func Regex(re *regexp.Regexp) Type[prism.Match, string, string]
- func RegexNamed(re *regexp.Regexp) Type[prism.NamedMatch, string, string]
- func Stat() Type[FileInfoWithPath, string, string]
- func String() Type[string, string, any]
- func TranscodeArray[T, O, I any](item Type[T, O, I]) Type[[]T, []O, []I]
- func TranscodeEither[L, R, OL, OR, IL, IR any](leftItem Type[L, OL, IL], rightItem Type[R, OR, IR]) Type[either.Either[L, R], either.Either[OL, OR], either.Either[IL, IR]]
- func URL() Type[*url.URL, string, string]
- type Validate
- type Validation
- type Void
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Codec ¶
Codec combines a Decoder and an Encoder for bidirectional transformations. It can decode input I to type A and encode type A to output O.
This is a simple struct that pairs a decoder with an encoder, providing the basic building blocks for bidirectional data transformation. Unlike the Type interface, Codec is a concrete struct without validation context or type checking capabilities.
Type Parameters:
- I: The input type to decode from
- O: The output type to encode to
- A: The intermediate type (decoded to, encoded from)
Fields:
- Decode: A decoder that transforms I to A
- Encode: An encoder that transforms A to O
Example:
A Codec[string, string, int] can decode strings to integers and encode integers back to strings.
Note: For most use cases, prefer using the Type interface which provides additional validation and type checking capabilities.
type Context ¶
type Context = validation.Context
Context provides contextual information for validation operations, such as the current path in a nested structure.
type Decode ¶
Decode is a function that decodes input I to type A with validation. It returns a Validation result directly.
The Decode type is defined as:
Reader[I, Validation[A]]
This is simpler than Validate as it doesn't require explicit context passing. The context is typically created automatically when the decoder is invoked.
Decode is used when:
- You don't need to manually manage validation context
- You want a simpler API for basic validation
- You're working at the top level of validation
Example:
A Decode[string, int] takes a string and returns a Validation[int] which is Either[Errors, int].
type Decoder ¶
type Decoder[I, A any] interface { Name() string Validate(I) Decode[Context, A] Decode(I) Validation[A] }
Decoder is an interface for types that can decode and validate input.
A Decoder transforms input of type I into a validated value of type A, providing detailed error information when validation fails. It supports both context-aware validation (via Validate) and direct decoding (via Decode).
Type Parameters:
- I: The input type to decode from
- A: The target type to decode to
Methods:
- Name(): Returns a descriptive name for this decoder (used in error messages)
- Validate(I): Returns a context-aware validation function that can track the path through nested structures
- Decode(I): Directly decodes input to a Validation result with a fresh context
The Validate method is more flexible as it returns a Reader that can be called with different contexts, while Decode is a convenience method that creates a new context automatically.
Example:
A Decoder[string, int] can decode strings to integers with validation.
type Encode ¶
Encode is a function that encodes type A to output O.
Encode is simply a Reader[A, O], which is a function from A to O. Encoders are pure functions with no error handling - they assume the input is valid.
Encoding is the inverse of decoding:
- Decoding: I -> Validation[A] (may fail)
- Encoding: A -> O (always succeeds)
Example:
An Encode[int, string] takes an integer and returns its string representation.
type Encoder ¶
type Encoder[A, O any] interface { // Encode transforms a value of type A into output format O. Encode(A) O }
Encoder is an interface for types that can encode values.
An Encoder transforms values of type A into output format O. This is the inverse operation of decoding, allowing bidirectional transformations.
Type Parameters:
- A: The source type to encode from
- O: The output type to encode to
Methods:
- Encode(A): Transforms a value of type A into output format O
Encoders are pure functions with no validation or error handling - they assume the input is valid. Validation should be performed during decoding.
Example:
An Encoder[int, string] can encode integers to their string representation.
type Endomorphism ¶
type Endomorphism[A any] = endomorphism.Endomorphism[A]
Endomorphism represents a function from type A to itself (A -> A). It forms a monoid under function composition.
func WithName ¶ added in v2.2.70
WithName returns an endomorphism that renames a codec without changing any of its behaviour.
The returned function accepts a Type[A, O, I] and returns a new codec with the specified name while preserving all validation, encoding, and type-checking logic. Only the value returned by Name() changes.
Type Parameters:
- A: The target type (what we decode to and encode from)
- O: The output type (what we encode to)
- I: The input type (what we decode from)
Parameters:
- name: The new name for the codec
Returns:
- An Endomorphism[Type[A, O, I]] that renames the codec
See Also:
- MakeType: for creating codecs with custom names from scratch
Example ¶
c := F.Pipe1(IntFromString(), WithName[int, string, string]("UserAge"))
fmt.Println(c.Name())
fmt.Println(c.Encode(25))
Output: UserAge 25
type FileInfoWithPath ¶ added in v2.3.72
type FileInfoWithPath interface {
fs.FileInfo
// AbsPath returns the absolute path of the file as resolved by filepath.Abs.
AbsPath() string
}
FileInfoWithPath extends fs.FileInfo with an absolute filesystem path. It combines all standard file metadata from fs.FileInfo with the resolved absolute path of the file, as returned by filepath.Abs.
This interface is the decoded value type produced by the Stat codec.
See Also:
- Stat: the codec that decodes a path string into a FileInfoWithPath
type Formattable ¶ added in v2.1.21
type Formattable = formatting.Formattable
Formattable represents a type that can be formatted as a string representation. It provides a way to obtain a human-readable description of a type or value.
type Iso ¶ added in v2.2.55
Iso represents an isomorphism - a bidirectional transformation between two types.
type Kleisli ¶ added in v2.2.12
Kleisli represents a Kleisli arrow in the codec context. It's a function that takes a value of type A and returns a codec Type[B, O, I].
This is the fundamental building block for codec transformations and compositions. Kleisli arrows allow you to:
- Chain codec operations
- Build dependent codecs (where the next codec depends on the previous result)
- Create codec pipelines
Type Parameters:
- A: The input type to the function
- B: The target type that the resulting codec decodes to
- O: The output type that the resulting codec encodes to
- I: The input type that the resulting codec decodes from
Example:
A Kleisli[string, int, string, string] takes a string and returns a codec that can decode strings to ints and encode ints to strings.
type Lens ¶ added in v2.2.27
Lens is an optic that focuses on a specific field within a product type S. It provides a way to get and set a field of type A within a structure of type S.
A Lens[S, A] represents a relationship between a source type S and a focus type A, where the focus always exists (unlike Optional which may not exist).
Lens operations:
- Get: Extract the field value A from structure S
- Set: Update the field value A in structure S, returning a new S
Lens laws:
- GetSet: If you get a value and then set it back, nothing changes Set(Get(s))(s) = s
- SetGet: If you set a value, you can get it back Get(Set(a)(s)) = a
- SetSet: Setting twice is the same as setting once with the final value Set(b)(Set(a)(s)) = Set(b)(s)
In the codec context, lenses are used with ApSL to build codecs for struct fields:
- Extract field values for encoding
- Update field values during validation
- Compose codec operations on nested structures
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 },
)
// Use with ApSL to build a codec
personCodec := F.Pipe1(
codec.Struct[Person]("Person"),
codec.ApSL(S.Monoid, nameLens, codec.String),
)
See also:
- ApSL: Applicative sequencing with lens
- Optional: For fields that may not exist
type Monoid ¶ added in v2.2.13
Monoid represents an algebraic structure with an associative binary operation and an identity element.
A Monoid[A] provides:
- Empty(): Returns the identity element
- Concat(A, A): Combines two values associatively
Monoid laws:
- Left Identity: Concat(Empty(), a) = a
- Right Identity: Concat(a, Empty()) = a
- Associativity: Concat(Concat(a, b), c) = Concat(a, Concat(b, c))
In the codec context, monoids are used to:
- Combine multiple codecs with specific semantics
- Build codec chains with fallback behavior (AltMonoid)
- Aggregate validation results (ApplicativeMonoid)
- Compose codec transformations
Example monoids for codecs:
- AltMonoid: First success wins (alternative semantics)
- ApplicativeMonoid: Combines successful results using inner monoid
- AlternativeMonoid: Combines applicative and alternative behaviors
func AltMonoid ¶ added in v2.2.13
func AltMonoid[A, O, I any](zero Lazy[Type[A, O, I]]) Monoid[Type[A, O, I]]
AltMonoid creates a Monoid instance for Type[A, O, I] using alternative semantics with a provided zero/default codec.
This function creates a monoid where:
- The first successful codec wins (no result combination)
- If the first fails during validation, the second is tried as a fallback
- If both fail, errors are aggregated
- The provided zero codec serves as the identity element
Unlike other monoid patterns, AltMonoid does NOT combine successful results - it always returns the first success. This makes it ideal for building fallback chains with default codecs, configuration loading from multiple sources, and parser combinators with alternatives.
Type Parameters ¶
- A: The target type that all codecs decode to
- O: The output type that all codecs encode to
- I: The input type that all codecs decode from
Parameters ¶
- zero: A lazy Type[A, O, I] that serves as the identity element. This is typically a codec that always succeeds with a default value, but can also be a failing codec if no default is appropriate.
Returns ¶
A Monoid[Type[A, O, I]] that combines codecs using alternative semantics where the first success wins.
Behavior Details ¶
The AltMonoid implements a "first success wins" strategy:
- **First succeeds**: Returns the first result, second is never evaluated
- **First fails, second succeeds**: Returns the second result
- **Both fail**: Aggregates errors from both validators
- **Concat with Empty**: The zero codec is used as fallback
- **Encoding**: Always uses the first codec's encoder
Example: Configuration Loading with Fallbacks ¶
import (
"github.com/IBM/fp-go/v2/optics/codec"
"github.com/IBM/fp-go/v2/array"
)
// Create a monoid with a default configuration
m := codec.AltMonoid(func() codec.Type[Config, string, string] {
return codec.MakeType(
"DefaultConfig",
codec.Is[Config](),
func(s string) codec.Decode[codec.Context, Config] {
return func(c codec.Context) codec.Validation[Config] {
return validation.Success(defaultConfig)
}
},
encodeConfig,
)
})
// Define codecs for different sources
fileCodec := loadFromFile("config.json")
envCodec := loadFromEnv()
defaultCodec := m.Empty()
// Try file, then env, then default
configCodec := array.MonadFold(
[]codec.Type[Config, string, string]{fileCodec, envCodec, defaultCodec},
m.Empty(),
m.Concat,
)
// Load configuration - tries each source in order
result := configCodec.Decode(input)
Example: Parser with Multiple Formats ¶
// Create a monoid for parsing dates in multiple formats
m := codec.AltMonoid(func() codec.Type[time.Time, string, string] {
return codec.Date(time.RFC3339) // default format
})
// Define parsers for different date formats
iso8601 := codec.Date("2006-01-02")
usFormat := codec.Date("01/02/2006")
euroFormat := codec.Date("02/01/2006")
// Combine: try ISO 8601, then US, then European, then RFC3339
flexibleDate := m.Concat(
m.Concat(
m.Concat(iso8601, usFormat),
euroFormat,
),
m.Empty(),
)
// Can parse any of these formats
result1 := flexibleDate.Decode("2024-03-15") // ISO 8601
result2 := flexibleDate.Decode("03/15/2024") // US format
result3 := flexibleDate.Decode("15/03/2024") // European format
Example: Integer Parsing with Default ¶
// Create a monoid with default value of 0
m := codec.AltMonoid(func() codec.Type[int, string, string] {
return codec.MakeType(
"DefaultZero",
codec.Is[int](),
func(s string) codec.Decode[codec.Context, int] {
return func(c codec.Context) codec.Validation[int] {
return validation.Success(0)
}
},
strconv.Itoa,
)
})
// Try parsing as int, fall back to 0
intOrZero := m.Concat(codec.IntFromString(), m.Empty())
result1 := intOrZero.Decode("42") // Success(42)
result2 := intOrZero.Decode("invalid") // Success(0) - uses default
Example: Error Aggregation ¶
// Both codecs fail - errors are aggregated
m := codec.AltMonoid(func() codec.Type[int, string, string] {
return codec.MakeType(
"NoDefault",
codec.Is[int](),
func(s string) codec.Decode[codec.Context, int] {
return func(c codec.Context) codec.Validation[int] {
return validation.FailureWithMessage[int](s, "no default available")(c)
}
},
strconv.Itoa,
)
})
failing1 := codec.MakeType(
"Failing1",
codec.Is[int](),
func(s string) codec.Decode[codec.Context, int] {
return func(c codec.Context) codec.Validation[int] {
return validation.FailureWithMessage[int](s, "error 1")(c)
}
},
strconv.Itoa,
)
failing2 := codec.MakeType(
"Failing2",
codec.Is[int](),
func(s string) codec.Decode[codec.Context, int] {
return func(c codec.Context) codec.Validation[int] {
return validation.FailureWithMessage[int](s, "error 2")(c)
}
},
strconv.Itoa,
)
combined := m.Concat(failing1, failing2)
result := combined.Decode("input")
// result contains errors: "error 1", "error 2", and "no default available"
Monoid Laws ¶
AltMonoid satisfies the monoid laws:
- **Left Identity**: m.Concat(m.Empty(), codec) ≡ codec
- **Right Identity**: m.Concat(codec, m.Empty()) ≡ codec (tries codec first, falls back to zero)
- **Associativity**: m.Concat(m.Concat(a, b), c) ≡ m.Concat(a, m.Concat(b, c))
Note: Due to the "first success wins" behavior, right identity means the zero is only used if the codec fails.
Use Cases ¶
- Configuration loading with multiple sources (file, env, default)
- Parsing data in multiple formats with fallbacks
- API versioning (try v2, fall back to v1, then default)
- Content negotiation (try JSON, then XML, then plain text)
- Validation with default values
- Parser combinators with alternative branches
Notes ¶
- The zero codec is lazily evaluated, only when needed
- First success short-circuits evaluation (subsequent codecs not tried)
- Error aggregation ensures all validation failures are reported
- Encoding always uses the first codec's encoder
- This follows the alternative functor laws
See Also ¶
- MonadAlt: The underlying alternative operation for two codecs
- Alt: The curried version for pipeline composition
- validate.AltMonoid: The validation-level alternative monoid
- decode.AltMonoid: The decode-level alternative monoid
type Operator ¶ added in v2.2.12
Operator is a specialized Kleisli arrow that transforms codecs. It takes a codec Type[A, O, I] and returns a new codec Type[B, O, I].
Operators are the primary way to build codec transformation pipelines. They enable functional composition of codec transformations using F.Pipe.
Type Parameters:
- A: The source type that the input codec decodes to
- B: The target type that the output codec decodes to
- O: The output type (same for both input and output codecs)
- I: The input type (same for both input and output codecs)
Common operators include:
- Map: Transforms the decoded value
- Chain: Sequences dependent codec operations
- Alt: Provides alternative fallback codecs
- Refine: Adds validation constraints
Example:
An Operator[int, PositiveInt, int, any] transforms a codec that decodes to int into a codec that decodes to PositiveInt (with validation).
Usage with F.Pipe:
codec := F.Pipe2(
baseCodec,
operator1, // Operator[A, B, O, I]
operator2, // Operator[B, C, O, I]
)
func Alt ¶ added in v2.2.12
func Alt[A, O, I any](second Lazy[Type[A, O, I]]) Operator[A, A, O, I]
Alt creates an operator that adds alternative fallback logic to a codec.
This is the curried, point-free version of MonadAlt. It returns a function that can be applied to codecs to add fallback behavior. This style is particularly useful for building codec transformation pipelines using function composition.
Alt implements the Alternative typeclass pattern, enabling "try this codec, or else try that codec" logic in a composable way.
Type Parameters ¶
- A: The target type that both codecs decode to
- O: The output type that both codecs encode to
- I: The input type that both codecs decode from
Parameters ¶
- second: A lazy codec that serves as the fallback. It's only evaluated if the first codec's validation fails.
Returns ¶
An Operator[A, A, O, I] that transforms codecs by adding alternative fallback logic. This operator can be applied to any Type[A, O, I] to create a new codec with fallback behavior.
Behavior ¶
When the returned operator is applied to a codec:
- **First succeeds**: Returns the first result, second is never evaluated
- **First fails, second succeeds**: Returns the second result
- **Both fail**: Aggregates errors from both validators
Example: Point-Free Style ¶
import (
F "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/optics/codec"
)
// Create a reusable fallback operator
withNumberFallback := codec.Alt(func() codec.Type[int, any, any] {
return codec.Int()
})
// Apply it to different codecs
flexibleInt1 := withNumberFallback(codec.IntFromString())
flexibleInt2 := withNumberFallback(codec.IntFromHex())
Example: Pipeline Composition ¶
// Build a codec pipeline with multiple fallbacks
flexibleCodec := F.Pipe2(
primaryCodec,
codec.Alt(func() codec.Type[T, O, I] { return fallback1 }),
codec.Alt(func() codec.Type[T, O, I] { return fallback2 }),
)
// Tries primary, then fallback1, then fallback2
Example: Reusable Transformations ¶
// Create a transformation that adds JSON fallback
withJSONFallback := codec.Alt(func() codec.Type[Config, string, string] {
return codec.JSONCodec[Config]()
})
// Apply to multiple codecs
yamlWithFallback := withJSONFallback(yamlCodec)
tomlWithFallback := withJSONFallback(tomlCodec)
Notes ¶
- This is the point-free style version of MonadAlt
- Useful for building transformation pipelines with F.Pipe
- The second codec is lazily evaluated for efficiency
- First success short-circuits evaluation
- Errors are aggregated when both fail
- Can be composed with other codec operators
See Also ¶
- MonadAlt: The direct application version
- validate.Alt: The underlying validation operation
- F.Pipe: For composing multiple operators
func AltW ¶ added in v2.3.90
AltW lifts two codecs of different decoded types into a single codec whose decoded type is Either[L, R]. The "W" suffix signals widening: the result type Either[L, R] is strictly wider than either branch alone.
AltW is the widening counterpart of Alt. Where Alt combines two Type[A, O, I] codecs (same decoded type, same encoder used), AltW combines a Type[R, O, I] and a Type[L, O, I] whose decoded types differ, producing a Type[Either[L, R], O, I].
When decoding input I:
- The Right branch (rightItem) is tried first.
- If it succeeds the value is wrapped in either.Right.
- If it fails the Left branch (leftItem) is tried.
- If the Left branch succeeds the value is wrapped in either.Left.
- If both fail, errors from both branches are accumulated.
When encoding Either[L, R]:
- Left(l) is encoded with leftItem.Encode.
- Right(r) is encoded with rightItem.Encode.
The resulting codec is named "AltW[<leftItem>, <rightItem>]".
Type Parameters:
- R: The Right decoded type (first explicit type argument)
- L: The Left decoded type
- O: The common output type for both branches
- I: The common input type for both branches
Parameters:
- leftItem: A Type[L, O, I] codec for the Left branch
Returns:
- Operator[R, Either[L, R], O, I]: an operator that accepts the Right branch codec and produces the combined Either codec
See Also:
- Alt: non-widening alternative that keeps the same decoded type
- EitherOf: boolean-predicate-gated Either codec
Example ¶
ExampleAltW demonstrates using AltW to build a codec that tries to decode the input as an int (Right branch) and falls back to a raw string (Left branch) when the int parse fails.
// AltW(leftCodec)(rightCodec): right branch tried first.
// Id[string]() accepts any string and encodes it as-is (Left branch).
// IntFromString() parses digit strings and encodes int back to string (Right branch).
c := AltW[int](Id[string]())(IntFromString())
// "42" parses successfully as int → Right(42) → encoded back as "42"
fmt.Println(c.Encode(either.Right[string](42)))
// "hello" fails int parsing → Left("hello") → encoded back as "hello"
fmt.Println(c.Encode(either.Left[int]("hello")))
Output: 42 hello
func ApSL ¶ added in v2.2.27
func ApSL[S, T, O, I any]( m Monoid[O], l Lens[S, T], fa Type[T, O, I], ) Operator[S, S, O, I]
ApSL creates an applicative sequencing operator for codecs using a lens.
This function implements the "ApS" (Applicative Sequencing) pattern for codecs, allowing you to build up complex codecs by combining a base codec with a field accessed through a lens. It's particularly useful for building struct codecs field-by-field in a composable way.
The function combines:
- Encoding: Extracts the field value using the lens, encodes it with fa, and combines it with the base encoding using the monoid
- Validation: Validates the field using the lens and combines the validation with the base validation
Type Parameters ¶
- S: The source struct type (what we're building a codec for)
- T: The field type accessed by the lens
- O: The output type for encoding (must have a monoid)
- I: The input type for decoding
Parameters ¶
- m: A Monoid[O] for combining encoded outputs
- l: A Lens[S, T] that focuses on a specific field in S
- fa: A Type[T, O, I] codec for the field type T
Returns ¶
An Operator[S, S, O, I] that transforms a base codec by adding the field specified by the lens.
How It Works ¶
1. **Encoding**: When encoding a value of type S:
- Extract the field T using l.Get
- Encode T to O using fa.Encode
- Combine with the base encoding using the monoid
2. **Validation**: When validating input I:
- Validate the field using fa.Validate through the lens
- Combine with the base validation
3. **Type Checking**: Preserves the base type checker
Example ¶
import (
"github.com/IBM/fp-go/v2/optics/codec"
"github.com/IBM/fp-go/v2/optics/lens"
S "github.com/IBM/fp-go/v2/string"
)
type Person struct {
Name string
Age int
}
// Lenses for Person fields
nameLens := lens.MakeLens(
func(p *Person) string { return p.Name },
func(p *Person, name string) *Person { p.Name = name; return p },
)
// Build a Person codec field by field
personCodec := F.Pipe1(
codec.Struct[Person]("Person"),
codec.ApSL(S.Monoid, nameLens, codec.String),
// ... add more fields
)
Use Cases ¶
- Building struct codecs incrementally
- Composing codecs for nested structures
- Creating type-safe serialization/deserialization
- Implementing Do-notation style codec construction
Notes ¶
- The monoid determines how encoded outputs are combined
- The lens must be total (handle all cases safely)
- This is typically used with other ApS functions to build complete codecs
- The name is automatically generated for debugging purposes
See also:
- validate.ApSL: The underlying validation combinator
- reader.ApplicativeMonoid: The monoid-based applicative instance
- Lens: The optic for accessing struct fields
func ApSO ¶ added in v2.2.28
func ApSO[S, T, O, I any]( m Monoid[O], o optional.Optional[S, T], fa Type[T, O, I], ) Operator[S, S, O, I]
ApSO creates an applicative sequencing operator for codecs using an optional.
This function implements the "ApS" (Applicative Sequencing) pattern for codecs with optional fields, allowing you to build up complex codecs by combining a base codec with a field that may or may not be present. It's particularly useful for building struct codecs with optional fields in a composable way.
The function combines:
- Encoding: Attempts to extract the optional field value, encodes it if present, and combines it with the base encoding using the monoid. If the field is absent, only the base encoding is used.
- Validation: Validates the optional field and combines the validation with the base validation using applicative semantics (error accumulation).
Type Parameters ¶
- S: The source struct type (what we're building a codec for)
- T: The optional field type accessed by the optional
- O: The output type for encoding (must have a monoid)
- I: The input type for decoding
Parameters ¶
- m: A Monoid[O] for combining encoded outputs
- o: An Optional[S, T] that focuses on a field in S that may not exist
- fa: A Type[T, O, I] codec for the optional field type T
Returns ¶
An Operator[S, S, O, I] that transforms a base codec by adding the optional field specified by the optional.
How It Works ¶
1. **Encoding**: When encoding a value of type S:
- Try to extract the optional field T using o.GetOption
- If present (Some(T)): Encode T to O using fa.Encode and combine with base using monoid
- If absent (None): Return only the base encoding unchanged
2. **Validation**: When validating input I:
- Validate the optional field using fa.Validate through o.Set
- Combine with the base validation using applicative semantics
- Accumulates all validation errors from both base and field
3. **Type Checking**: Preserves the base type checker
Difference from ApSL ¶
Unlike ApSL which works with required fields via Lens, ApSO handles optional fields:
- ApSL: Field always exists, always encoded
- ApSO: Field may not exist, only encoded when present
- ApSO uses Optional.GetOption which returns Option[T]
- ApSO gracefully handles missing fields without errors
Example ¶
import (
"github.com/IBM/fp-go/v2/optics/codec"
"github.com/IBM/fp-go/v2/optics/optional"
S "github.com/IBM/fp-go/v2/string"
)
type Person struct {
Name string
Nickname *string // Optional field
}
// Optional for Person.Nickname
nicknameOpt := optional.MakeOptional(
func(p Person) option.Option[string] {
if p.Nickname != nil {
return option.Some(*p.Nickname)
}
return option.None[string]()
},
func(p Person, nick string) Person {
p.Nickname = &nick
return p
},
)
// Build a Person codec with optional nickname
personCodec := F.Pipe1(
codec.Struct[Person]("Person"),
codec.ApSO(S.Monoid, nicknameOpt, codec.String),
)
// Encoding with nickname present
p1 := Person{Name: "Alice", Nickname: ptr("Ali")}
encoded1 := personCodec.Encode(p1) // Includes nickname
// Encoding with nickname absent
p2 := Person{Name: "Bob", Nickname: nil}
encoded2 := personCodec.Encode(p2) // No nickname in output
Use Cases ¶
- Building struct codecs with optional/nullable fields
- Handling pointer fields that may be nil
- Composing codecs for structures with optional nested data
- Creating flexible serialization that omits absent fields
Notes ¶
- The monoid determines how encoded outputs are combined when field is present
- When the optional field is absent, encoding returns base encoding unchanged
- Validation still accumulates errors even for optional fields
- The name is automatically generated for debugging purposes
See Also ¶
- ApSL: For required fields using Lens
- validate.ApS: The underlying validation combinator
- Optional: The optic for accessing optional fields
func Bind ¶ added in v2.2.35
func Bind[S, T, O, I any]( m Monoid[O], l Lens[S, T], f Kleisli[S, T, O, I], ) Operator[S, S, O, I]
Bind creates a monadic sequencing operator for codecs using a lens and a Kleisli arrow.
This function implements the "Bind" (monadic bind / chain) pattern for codecs, allowing you to build up complex codecs where the codec for a field depends on the current decoded value of the struct. Unlike ApSL which uses a fixed field codec, Bind accepts a Kleisli arrow — a function from the current struct value S to a Type[T, O, I] — enabling context-sensitive codec construction.
The function combines:
- Encoding: Evaluates the Kleisli arrow f on the current struct value s to obtain the field codec, extracts the field T using the lens, encodes it with that codec, and combines it with the base encoding using the monoid.
- Validation: Validates the base struct first (monadic sequencing), then uses the Kleisli arrow to obtain the field codec for the decoded struct value, and validates the field through the lens. Errors are propagated but NOT accumulated (fail-fast semantics, unlike ApSL which accumulates errors).
Type Parameters ¶
- S: The source struct type (what we're building a codec for)
- T: The field type accessed by the lens
- O: The output type for encoding (must have a monoid)
- I: The input type for decoding
Parameters ¶
- m: A Monoid[O] for combining encoded outputs
- l: A Lens[S, T] that focuses on a specific field in S
- f: A Kleisli[S, T, O, I] — a function from S to Type[T, O, I] — that produces the field codec based on the current struct value
Returns ¶
An Operator[S, S, O, I] that transforms a base codec by adding the field specified by the lens, where the field codec is determined by the Kleisli arrow.
How It Works ¶
1. **Encoding**: When encoding a value of type S:
- Evaluate f(s) to obtain the field codec fa
- Extract the field T using l.Get
- Encode T to O using fa.Encode
- Combine with the base encoding using the monoid
2. **Validation**: When validating input I:
- Run the base validation to obtain a decoded S (fail-fast: stop on base failure)
- For the decoded S, evaluate f(s) to obtain the field codec fa
- Validate the input I using fa.Validate
- Set the validated T into S using l.Set
3. **Type Checking**: Preserves the base type checker
Difference from ApSL ¶
Unlike ApSL which uses a fixed field codec:
- ApSL: Field codec is fixed at construction time; errors are accumulated
- Bind: Field codec depends on the current struct value (Kleisli arrow); validation uses monadic sequencing (fail-fast on base failure)
- Bind is more powerful but less parallel than ApSL
Example ¶
import (
"github.com/IBM/fp-go/v2/optics/codec"
"github.com/IBM/fp-go/v2/optics/lens"
S "github.com/IBM/fp-go/v2/string"
)
type Config struct {
Mode string
Value int
}
modeLens := lens.MakeLens(
func(c Config) string { return c.Mode },
func(c Config, mode string) Config { c.Mode = mode; return c },
)
// Build a Config codec where the Value codec depends on the Mode
configCodec := F.Pipe1(
codec.Struct[Config]("Config"),
codec.Bind(S.Monoid, modeLens, func(c Config) codec.Type[string, string, any] {
return codec.String()
}),
)
Use Cases ¶
- Building codecs where a field's codec depends on another field's value
- Implementing discriminated unions or tagged variants
- Context-sensitive validation (e.g., validate field B differently based on field A)
- Dependent type-like patterns in codec construction
Notes ¶
- The monoid determines how encoded outputs are combined
- The lens must be total (handle all cases safely)
- Validation uses monadic (fail-fast) sequencing: if the base codec fails, the Kleisli arrow is never evaluated
- The name is automatically generated for debugging purposes
See also:
- ApSL: Applicative sequencing with a fixed lens codec (error accumulation)
- Kleisli: The function type from S to Type[T, O, I]
- validate.Bind: The underlying validate-level bind combinator
func EitherOf ¶ added in v2.3.90
func EitherOf[L, R, O, I any]( m Monoid[O], onLeft Type[L, O, I], onRight Type[R, O, I], ) Operator[bool, either.Either[L, R], O, I]
EitherOf lifts two codecs — one for L and one for R — into a codec Type[Either[L, R], O, I] by dispatching on a boolean predicate codec.
When decoding input I:
- The predicate pred is validated against the input first.
- If pred decodes to true, the right codec onRight is invoked and its result is wrapped in either.Right.
- If pred decodes to false, the left codec onLeft is invoked and its result is wrapped in either.Left.
When encoding Either[L, R]:
- If the value is Right(r), r is encoded with onRight.Encode and true is encoded with pred.Encode; the two outputs are combined with the monoid.
- If the value is Left(l), l is encoded with onLeft.Encode and false is encoded with pred.Encode; the two outputs are combined with the monoid.
The resulting codec is named "EitherOf[<pred> x (<onLeft>|<onRight>)]".
Type Parameters:
- L: The type decoded by the left codec onLeft
- R: The type decoded by the right codec onRight
- O: The output type produced by the predicate and both branch codecs
- I: The input type consumed by the predicate and both branch codecs
Parameters:
- m: A Monoid[O] used to combine the encoded predicate output and the encoded branch output
- onLeft: A Type[L, O, I] that decodes and encodes the Left branch
- onRight: A Type[R, O, I] that decodes and encodes the Right branch
Returns:
- An Operator[bool, Either[L, R], O, I] that transforms a Type[bool, O, I] predicate codec into a Type[Either[L, R], O, I]
See Also:
- Optional: Boolean-gated codec that lifts a value into Option
- Either: Untagged either codec that tries both branches by structure
Example ¶
ExampleEitherOf demonstrates using EitherOf to build a codec that dispatches between a string (Left) and an int (Right) branch based on a boolean flag parsed from the input string.
The input format is "<flag>:<value>" where flag is "true" or "false". "true" routes to the right (int) branch. "false" routes to the left (string) branch.
// BoolFromString decodes "true"/"false" and encodes bool back to string.
// Id[string]() encodes string → string (identity).
// IntFromString() decodes a string digit and encodes int → string.
//
// We wire them together so:
// pred=true → Right branch (int)
// pred=false → Left branch (string)
c := EitherOf(S.Monoid, Id[string](), IntFromString())(BoolFromString())
// Encode Right(42): IntFromString encodes 42 → "42", pred encodes true → "true"
// S.Monoid concatenates: "42" + "true" = "42true"
fmt.Println(c.Encode(either.Right[string](42)))
// Encode Left("err"): Id encodes "err" → "err", pred encodes false → "false"
// S.Monoid concatenates: "err" + "false" = "errfalse"
fmt.Println(c.Encode(either.Left[int]("err")))
Output: 42true errfalse
func Optional ¶ added in v2.2.28
Optional lifts a codec Type[A, O, I] into a codec Type[Option[A], O, I] by gating decoding on a boolean predicate codec.
When decoding input I:
- The predicate pred is validated against the input first.
- If pred decodes to true, the inner codec onSome is invoked and its result is wrapped in option.Some.
- If pred decodes to false (or the input does not match), the result is option.None and the monoid empty value is used for the output.
When encoding Option[A]:
- If the option is Some(a), a is encoded with onSome.Encode and the boolean true is encoded with pred.Encode; the two outputs are combined with the monoid.
- If the option is None, the monoid empty value is used for the inner encoding and false is encoded with pred.Encode; the two outputs are combined with the monoid.
The resulting codec is named "Optional[<pred> x <onSome>]".
Type Parameters:
- A: The type decoded by the inner codec onSome
- O: The output type produced by both the predicate and the inner codec
- I: The input type consumed by both the predicate and the inner codec
Parameters:
- m: A Monoid[O] used to combine the encoded predicate output and the encoded value output
- pred: A Type[bool, O, I] that decodes the presence flag (bool) and encodes it back to O
Returns:
- An Operator[A, Option[A], O, I] that transforms a Type[A, O, I] into a Type[Option[A], O, I]
See Also:
- ApSO: Applicative sequencing for optional struct fields via Optional optic
- Do: Entry point for do-notation style codec construction
func Pipe ¶
func Pipe[O, I, A, B any](ab Type[B, A, A]) Operator[A, B, O, I]
Pipe composes two Types, creating a pipeline where:
- Decoding: I -> A -> B (decode with 'this', then validate with 'ab')
- Encoding: B -> A -> O (encode with 'ab', then encode with 'this')
This allows building complex codecs from simpler ones.
Example:
stringToInt := codec.MakeType(...) // Type[int, string, string] intToPositive := codec.MakeType(...) // Type[PositiveInt, int, int] composed := codec.Pipe(intToPositive)(stringToInt) // Type[PositiveInt, string, string]
type Prism ¶ added in v2.1.19
Prism is an optic that focuses on a part of a sum type S that may or may not contain a value of type A. It provides a way to preview and review values.
func TypeToPrism ¶ added in v2.1.19
func TypeToPrism[S, A any](t Type[A, S, S]) Prism[S, A]
TypeToPrism converts a Type codec into a Prism optic.
A Type[A, S, S] represents a bidirectional codec that can decode S to A (with validation) and encode A back to S. A Prism[S, A] is an optic that can optionally extract an A from S and always construct an S from an A.
This conversion bridges the codec and optics worlds, allowing you to use validation-based codecs as prisms for functional optics composition.
Type Parameters:
- S: The source/encoded type (both input and output)
- A: The decoded/focus type
Parameters:
- t: A Type[A, S, S] codec where:
- Decode: S → Validation[A] (may fail with validation errors)
- Encode: A → S (always succeeds)
- Name: Provides a descriptive name for the type
Returns:
- A Prism[S, A] where:
- GetOption: S → Option[A] (Some if decode succeeds, None if validation fails)
- ReverseGet: A → S (uses the codec's Encode function)
- Name: Inherited from the Type's name
The conversion works as follows:
- GetOption: Decodes the value and converts validation result to Option (Right(a) → Some(a), Left(errors) → None)
- ReverseGet: Directly uses the Type's Encode function
- Name: Preserves the Type's descriptive name
Example:
// Create a codec for positive integers
positiveInt := codec.MakeType[int, int, int](
"PositiveInt",
func(i any) result.Result[int] { ... },
func(i int) codec.Validate[int] {
if i <= 0 {
return validation.FailureWithMessage(i, "must be positive")
}
return validation.Success(i)
},
func(i int) int { return i },
)
// Convert to prism
prism := codec.TypeToPrism(positiveInt)
// Use as prism
value := prism.GetOption(42) // Some(42) - validation succeeds
value = prism.GetOption(-5) // None - validation fails
result := prism.ReverseGet(10) // 10 - encoding always succeeds
Use cases:
- Composing codecs with other optics (lenses, prisms, traversals)
- Using validation logic in optics pipelines
- Building complex data transformations with functional composition
- Integrating type-safe parsing with optics-based data access
Note: The prism's GetOption will return None for any validation failure, discarding the specific error details. If you need error information, use the Type's Decode method directly instead.
type Reader ¶
Reader represents a computation that depends on an environment R and produces a value A.
type ReaderResult ¶
type ReaderResult[R, A any] = readerresult.ReaderResult[R, A]
ReaderResult represents a computation that depends on an environment R, produces a value A, and may fail with an error.
func Is ¶
func Is[T any]() ReaderResult[any, T]
Is checks if a value can be converted to type T. Returns Some(value) if the conversion succeeds, None otherwise. This is a type-safe cast operation.
type Refinement ¶ added in v2.1.19
Refinement represents the concept that B is a specialized type of A. It's an alias for Prism[A, B], providing a semantic name for type refinement operations.
A refinement allows you to:
- Preview: Try to extract a B from an A (may fail if A is not a B)
- Review: Inject a B back into an A
This is useful for working with subtypes, validated types, or constrained types.
Example:
- Refinement[int, PositiveInt] - refines int to positive integers only
- Refinement[string, NonEmptyString] - refines string to non-empty strings
- Refinement[any, User] - refines any to User type
type Semigroup ¶ added in v2.2.29
Semigroup represents an algebraic structure with an associative binary operation.
A Semigroup[A] provides:
- Concat(A, A): Combines two values associatively
Semigroup law:
- Associativity: Concat(Concat(a, b), c) = Concat(a, Concat(b, c))
Unlike Monoid, Semigroup does not require an identity element (Empty). This makes it more general but less powerful for certain operations.
In the codec context, semigroups are used to:
- Combine validation errors
- Merge partial results
- Aggregate codec outputs
Example semigroups:
- String concatenation (without empty string)
- Array concatenation (without empty array)
- Error accumulation
Note: Every Monoid is also a Semigroup, but not every Semigroup is a Monoid.
type Type ¶
type Type[A, O, I any] interface { Formattable Decoder[I, A] Encoder[A, O] AsDecoder() Decoder[I, A] AsEncoder() Encoder[A, O] Is(any) Result[A] }
Type is a bidirectional codec that combines encoding, decoding, validation, and type checking capabilities. It represents a complete specification of how to work with a particular type.
Type is the central abstraction in the codec package, providing:
- Decoding: Transform input I to validated type A
- Encoding: Transform type A to output O
- Validation: Context-aware validation with detailed error reporting
- Type Checking: Runtime type verification via Is()
- Formatting: Human-readable type descriptions via Name()
Type Parameters:
- A: The target type (what we decode to and encode from)
- O: The output type (what we encode to)
- I: The input type (what we decode from)
Common patterns:
- Type[A, A, A]: Identity codec (no transformation)
- Type[A, string, string]: String-based serialization
- Type[A, any, any]: Generic codec accepting any input/output
- Type[A, JSON, JSON]: JSON codec
Methods:
- Name(): Returns the codec's descriptive name
- Validate(I): Returns context-aware validation function
- Decode(I): Decodes input with automatic context creation
- Encode(A): Encodes value to output format
- AsDecoder(): Returns this Type as a Decoder interface
- AsEncoder(): Returns this Type as an Encoder interface
- Is(any): Checks if a value can be converted to type A
Example usage:
intCodec := codec.Int() // Type[int, int, any]
stringCodec := codec.String() // Type[string, string, any]
intFromString := codec.IntFromString() // Type[int, string, string]
// Decode
result := intFromString.Decode("42") // Validation[int]
// Encode
str := intFromString.Encode(42) // "42"
// Type check
isInt := intCodec.Is(42) // Right(42)
notInt := intCodec.Is("42") // Left(error)
Composition:
Types can be composed using operators like Alt, Map, Chain, and Pipe to build complex codecs from simpler ones.
func Array ¶ added in v2.1.19
Array creates a Type for array/slice values with elements of type T. It validates that input is an array, slice, or string, and validates each element using the provided item Type. During encoding, it maps the encode function over all elements.
Type Parameters:
- T: The type of elements in the decoded array
- O: The type of elements in the encoded array
Parameters:
- item: A Type[T, O, any] that defines how to validate/encode individual elements
Returns:
- A Type[[]T, []O, any] that can validate, decode, and encode array values
The function handles:
- Native Go slices of type []T (passed through directly)
- reflect.Array, reflect.Slice, reflect.String (validated element by element)
- Collects all validation errors from individual elements
- Provides detailed context for each element's position in error messages
Example:
intArray := codec.Array(codec.Int())
result := intArray.Decode([]int{1, 2, 3}) // Success: Right([1, 2, 3])
result := intArray.Decode([]any{1, "2", 3}) // Failure: validation error at index 1
encoded := intArray.Encode([]int{1, 2, 3}) // Returns: []int{1, 2, 3}
stringArray := codec.Array(codec.String())
result := stringArray.Decode([]string{"a", "b"}) // Success: Right(["a", "b"])
result := stringArray.Decode("hello") // Success: Right(["h", "e", "l", "l", "o"])
func BindTo ¶ added in v2.3.80
func BindTo[S, T, O, I any]( m Monoid[O], p Prism[S, T], t Type[T, O, I], ) Type[S, O, I]
BindTo lifts a Type[T, O, I] into a Type[S, O, I] using a Prism.
This is the codec-level analogue of the do-notation BindTo combinator. Given a Prism that focuses on the T variant of a sum type S, it creates a codec for S by:
- Decoding: validates and decodes I to T using the inner codec, then constructs S from T using the prism's ReverseGet.
- Encoding: if S contains a T (GetOption succeeds), encodes T to O using the inner codec; otherwise returns the monoid's empty value.
- Type checking: accepts any value (uses the universal Is[S] checker).
BindTo is the entry point for building a sum-type codec. It is typically followed by Bind or ApSL operators that add further fields.
Type Parameters ¶
- S: The sum type (or wrapper type) being constructed
- T: The variant type focused on by the prism
- O: The output type for encoding (must have a monoid)
- I: The input type for decoding
Parameters ¶
- m: A Monoid[O] used to produce the empty output when the prism does not match
- p: A Prism[S, T] whose ReverseGet constructs S from T and whose GetOption extracts T from S for encoding
- t: The Type[T, O, I] codec for the inner variant type T
Returns ¶
A Type[S, O, I] that decodes I to S (via T) and encodes S to O.
Example ¶
import (
"github.com/IBM/fp-go/v2/optics/codec"
"github.com/IBM/fp-go/v2/optics/prism"
"github.com/IBM/fp-go/v2/option"
S "github.com/IBM/fp-go/v2/string"
)
type Wrapper struct{ Value string }
wrapperPrism := prism.MakePrism(
func(w Wrapper) option.Option[string] { return option.Some(w.Value) },
func(s string) Wrapper { return Wrapper{Value: s} },
)
wrapperCodec := codec.BindTo(S.Monoid, wrapperPrism, codec.String())
// wrapperCodec is a Type[Wrapper, string, any]
See Also ¶
- BindTo (readerioeither): The monadic BindTo for effectful pipelines
- Bind: Adds further fields to a codec after BindTo initialises it
- validate.BindTo: The underlying validate-level BindTo combinator
func Bool ¶
Bool creates a Type for bool values. It validates that input is a bool type and provides identity encoding/decoding. This is a simple type that accepts any input and validates it's a bool.
Returns:
- A Type[bool, bool, any] that can validate, decode, and encode bool values
Example:
boolType := codec.Bool() result := boolType.Decode(true) // Success: Right(true) result := boolType.Decode(1) // Failure: Left(validation errors) encoded := boolType.Encode(false) // Returns: false
func BoolFromString ¶ added in v2.2.37
BoolFromString creates a bidirectional codec for parsing boolean values from strings.
The codec decodes by calling strconv.ParseBool and encodes by calling strconv.FormatBool. The accepted string values (per strconv.ParseBool) are:
- true: "1", "t", "T", "TRUE", "true", "True"
- false: "0", "f", "F", "FALSE", "false", "False"
Note that encoding always produces "true" or "false" regardless of which accepted input form was decoded (e.g. "1" decodes to true but re-encodes as "true").
Returns:
- A Type[bool, string, string] codec
Example ¶
c := BoolFromString() fmt.Println(c.Encode(true)) fmt.Println(c.Encode(false))
Output: true false
func Date ¶ added in v2.2.1
Date creates a bidirectional codec for date/time parsing and formatting with a specific layout.
The codec decodes by calling time.Parse(layout, s) and encodes by calling time.Time.Format(layout). The codec name is always "Date" regardless of the layout.
Parameters:
- layout: The time layout string (e.g., "2006-01-02", time.RFC3339). See the time package documentation for layout format details.
Returns:
- A Type[time.Time, string, string] codec
Example ¶
c := Date("2006-01-02")
tm := time.Date(2024, 3, 15, 0, 0, 0, 0, time.UTC)
fmt.Println(c.Encode(tm))
Output: 2024-03-15
func Do ¶ added in v2.2.36
func Do[I, A, O any](e Lazy[Pair[O, A]]) Type[A, O, I]
Do creates the initial empty codec to be used as the starting point for do-notation style codec construction.
This is the entry point for building up a struct codec field-by-field using the applicative and monadic sequencing operators ApSL, ApSO, and Bind. It wraps Empty and lifts a lazily-evaluated default Pair[O, A] into a Type[A, O, I] that ignores its input and always succeeds with the default value.
Type Parameters ¶
- I: The input type for decoding (what the codec reads from)
- A: The target struct type being built up (what the codec decodes to)
- O: The output type for encoding (what the codec writes to)
Parameters ¶
- e: A Lazy[Pair[O, A]] providing the initial default values:
- pair.Head(e()): The default encoded output O (e.g. an empty monoid value)
- pair.Tail(e()): The initial zero value of the struct A (e.g. MyStruct{})
Returns ¶
- A Type[A, O, I] that always decodes to the default A and encodes to the default O, regardless of input. This is then transformed by chaining ApSL, ApSO, or Bind operators to add fields one by one.
Example Usage ¶
Building a struct codec using do-notation style:
import (
"github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/lazy"
"github.com/IBM/fp-go/v2/optics/codec"
"github.com/IBM/fp-go/v2/optics/lens"
"github.com/IBM/fp-go/v2/pair"
S "github.com/IBM/fp-go/v2/string"
)
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 },
)
ageLens := lens.MakeLens(
func(p Person) int { return p.Age },
func(p Person, age int) Person { p.Age = age; return p },
)
personCodec := F.Pipe2(
codec.Do[any, Person, string](lazy.Of(pair.MakePair("", Person{}))),
codec.ApSL(S.Monoid, nameLens, codec.String()),
codec.ApSL(S.Monoid, ageLens, codec.Int()),
)
Notes ¶
- Do is typically the first call in a codec pipeline, followed by ApSL, ApSO, or Bind
- The lazy pair should use the monoid's empty value for O and the zero value for A
- For convenience, use Struct to create the initial codec for named struct types
See Also ¶
- Empty: The underlying codec constructor that Do delegates to
- ApSL: Applicative sequencing for required struct fields via Lens
- ApSO: Applicative sequencing for optional struct fields via Optional
- Bind: Monadic sequencing for context-dependent field codecs
- BindTo: Alternative starting point when the target type is a sum type accessed via a Prism
func Empty ¶ added in v2.2.29
func Empty[I, A, O any](e Lazy[Pair[O, A]]) Type[A, O, I]
Empty creates a Type codec that ignores input during decoding and uses a default value, and ignores the value during encoding, using a default output.
This codec is useful for:
- Providing default values for optional fields
- Creating placeholder codecs in generic contexts
- Implementing constant codecs that always produce the same value
- Building codecs for phantom types or unit-like types
The codec uses a lazily-evaluated Pair[O, A] to provide both the default output for encoding and the default value for decoding. The lazy evaluation ensures that the defaults are only computed when needed.
Type Parameters ¶
- A: The target type (what we decode to and encode from)
- O: The output type (what we encode to)
- I: The input type (what we decode from, but is ignored)
Parameters ¶
- e: A Lazy[Pair[O, A]] that provides the default values:
- pair.Head(e()): The default output value O used during encoding
- pair.Tail(e()): The default decoded value A used during decoding
Returns ¶
- A Type[A, O, I] that:
- Decode: Always succeeds and returns the default value A, ignoring input I
- Encode: Always returns the default output O, ignoring the input value A
- Is: Checks if a value is of type A (standard type checking)
- Name: Returns "Empty"
Behavior ¶
Decoding:
- Ignores the input value completely
- Always succeeds with validation.Success
- Returns the default value from pair.Tail(e())
Encoding:
- Ignores the input value completely
- Always returns the default output from pair.Head(e())
Example Usage ¶
Creating a codec with default values:
// Create a codec that always decodes to 42 and encodes to "default"
defaultCodec := codec.Empty[int, string, any](lazy.Of(pair.MakePair("default", 42)))
// Decode always returns 42, regardless of input
result := defaultCodec.Decode("anything") // Success: Right(42)
result = defaultCodec.Decode(123) // Success: Right(42)
result = defaultCodec.Decode(nil) // Success: Right(42)
// Encode always returns "default", regardless of input
encoded := defaultCodec.Encode(100) // Returns: "default"
encoded = defaultCodec.Encode(0) // Returns: "default"
Using with struct fields for default values:
type Config struct {
Timeout int
Retries int
}
// Codec that provides default retries value
defaultRetries := codec.Empty[int, int, any](lazy.Of(pair.MakePair(3, 3)))
configCodec := F.Pipe2(
codec.Struct[Config]("Config"),
codec.ApSL(S.Monoid, timeoutLens, codec.Int()),
codec.ApSL(S.Monoid, retriesLens, defaultRetries),
)
Creating a unit-like codec:
// Codec for a unit type that always produces Void
unitCodec := codec.Empty[function.Void, function.Void, any](
lazy.Of(pair.MakePair(function.VOID, function.VOID)),
)
Use Cases ¶
- Default values: Provide fallback values when decoding optional fields
- Constant codecs: Always produce the same value regardless of input
- Placeholder codecs: Use in generic contexts where a codec is required but not used
- Unit types: Encode/decode unit-like types that carry no information
- Testing: Create simple codecs for testing codec composition
Notes ¶
- The lazy evaluation of the Pair ensures defaults are only computed when needed
- Both encoding and decoding always succeed (no validation errors)
- The input values are completely ignored in both directions
- The Is method still performs standard type checking for type A
- This codec is useful in applicative composition where some fields have defaults
See also:
- Id: For identity codecs that preserve values
- MakeType: For creating custom codecs with validation logic
- BindTo: Alternative starting point when the target type is a sum type accessed via a Prism
func FromIso ¶ added in v2.2.55
func FromIso[A, I any](iso Iso[I, A]) Type[A, I, I]
FromIso creates a Type codec from an Iso (isomorphism).
An isomorphism represents a bidirectional transformation between types I and A without any loss of information. This function converts an Iso[I, A] into a Type[A, I, I] codec that can validate, decode, and encode values using the isomorphism's transformations.
The resulting codec:
- Decode: Uses iso.Get to transform I → A, always succeeds (no validation)
- Encode: Uses iso.ReverseGet to transform A → I
- Validation: Always succeeds since isomorphisms are lossless transformations
- Type checking: Uses standard type checking for type A
This is particularly useful for:
- Creating codecs for newtype patterns (wrapping/unwrapping types)
- Building codecs for types with lossless conversions
- Composing with other codecs using Pipe or other operators
- Implementing bidirectional transformations in codec pipelines
Type Parameters ¶
- A: The target type (what we decode to and encode from)
- I: The input/output type (what we decode from and encode to)
Parameters ¶
- iso: An Iso[I, A] that defines the bidirectional transformation:
- Get: I → A (converts input to target type)
- ReverseGet: A → I (converts target back to input type)
Returns ¶
- A Type[A, I, I] codec where:
- Decode: I → Validation[A] - transforms using iso.Get, always succeeds
- Encode: A → I - transforms using iso.ReverseGet
- Is: Checks if a value is of type A
- Name: Returns "FromIso[iso_string_representation]"
Behavior ¶
Decoding:
- Applies iso.Get to transform the input value
- Wraps the result in decode.Of (always successful validation)
- No validation errors can occur since isomorphisms are lossless
Encoding:
- Applies iso.ReverseGet to transform back to the input type
- Always succeeds as isomorphisms guarantee reversibility
Example Usage ¶
Creating a codec for a newtype pattern:
type UserId int
// Define an isomorphism between int and UserId
userIdIso := iso.MakeIso(
func(id UserId) int { return int(id) },
func(i int) UserId { return UserId(i) },
)
// Create a codec from the isomorphism
userIdCodec := codec.FromIso[int, UserId](userIdIso)
// Decode: UserId → int
result := userIdCodec.Decode(UserId(42)) // Success: Right(42)
// Encode: int → UserId
encoded := userIdCodec.Encode(42) // Returns: UserId(42)
Using with temperature conversions:
type Celsius float64
type Fahrenheit float64
celsiusToFahrenheit := iso.MakeIso(
func(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) },
func(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) },
)
tempCodec := codec.FromIso[Fahrenheit, Celsius](celsiusToFahrenheit)
// Decode: Celsius → Fahrenheit
result := tempCodec.Decode(Celsius(20)) // Success: Right(68°F)
// Encode: Fahrenheit → Celsius
encoded := tempCodec.Encode(Fahrenheit(68)) // Returns: 20°C
Composing with other codecs:
type Email string
type ValidatedEmail struct{ value Email }
emailIso := iso.MakeIso(
func(ve ValidatedEmail) Email { return ve.value },
func(e Email) ValidatedEmail { return ValidatedEmail{value: e} },
)
// Compose with string codec for validation
emailCodec := F.Pipe2(
codec.String(), // Type[string, string, any]
codec.Pipe(codec.FromIso[Email, string]( // Add string → Email iso
iso.MakeIso(
func(s string) Email { return Email(s) },
func(e Email) string { return string(e) },
),
)),
codec.Pipe(codec.FromIso[ValidatedEmail, Email](emailIso)), // Add Email → ValidatedEmail iso
)
Use Cases ¶
- Newtype patterns: Wrapping primitive types for type safety
- Unit conversions: Temperature, distance, time, etc.
- Format transformations: Between equivalent representations
- Type aliasing: Creating semantic types from base types
- Codec composition: Building complex codecs from simple isomorphisms
Notes ¶
- Isomorphisms must satisfy the round-trip laws:
- iso.ReverseGet(iso.Get(i)) == i
- iso.Get(iso.ReverseGet(a)) == a
- Validation always succeeds since isomorphisms are lossless
- The codec name includes the isomorphism's string representation
- Type checking is performed using the standard Is[A]() function
- This codec is ideal for lossless transformations without validation logic
See Also ¶
- iso.Iso: The isomorphism type used by this function
- iso.MakeIso: Constructor for creating isomorphisms
- Pipe: For composing this codec with other codecs
- MakeType: For creating codecs with custom validation logic
func FromNonZero ¶ added in v2.2.70
func FromNonZero[T comparable]() Type[T, T, T]
FromNonZero creates a bidirectional codec that validates the input is not equal to the zero value of T.
The codec decodes by asserting the input is non-zero (using a prism built from prism.FromNonZero) and encodes by returning the value unchanged. Decoding fails with a validation error when the input equals the zero value of T (0 for numeric types, "" for string, false for bool, nil for pointers).
Type Parameters:
- T: A comparable type
Returns:
- A Type[T, T, T] codec that validates non-zero values
See Also:
- NonEmptyString: specialised version for strings with a descriptive name
- FromRefinement: general function for creating codecs from prisms
Example ¶
c := FromNonZero[int]() fmt.Println(c.Encode(42))
Output: 42
func FromRefinement ¶ added in v2.1.19
func FromRefinement[A, B any](refinement Refinement[A, B]) Type[B, A, A]
FromRefinement creates a Type codec from a Refinement (Prism).
A Refinement[A, B] represents the concept that B is a specialized/refined version of A. For example, PositiveInt is a refinement of int, or NonEmptyString is a refinement of string. This function converts a Prism[A, B] into a Type[B, A, A] codec that can validate and transform between the base type A and the refined type B.
Type Parameters:
- A: The base/broader type (e.g., int, string)
- B: The refined/specialized type (e.g., PositiveInt, NonEmptyString)
Parameters:
- refinement: A Refinement[A, B] (which is a Prism[A, B]) that defines:
- GetOption: A → Option[B] - attempts to refine A to B (may fail if refinement conditions aren't met)
- ReverseGet: B → A - converts refined type back to base type (always succeeds)
Returns:
- A Type[B, A, A] codec where:
- Decode: A → Validation[B] - validates that A satisfies refinement conditions and produces B
- Encode: B → A - converts refined type back to base type using ReverseGet
- Is: Checks if a value is of type B
- Name: Descriptive name including the refinement's string representation
The codec:
- Uses the refinement's GetOption for validation during decoding
- Returns validation errors if the refinement conditions are not met
- Uses the refinement's ReverseGet for encoding (always succeeds)
- Provides context-aware error messages indicating why refinement failed
Example:
// Define a refinement for positive integers
positiveIntPrism := prism.MakePrismWithName(
func(n int) option.Option[int] {
if n > 0 {
return option.Some(n)
}
return option.None[int]()
},
func(n int) int { return n },
"PositiveInt",
)
// Create a codec from the refinement
positiveIntCodec := codec.FromRefinement[int, int](positiveIntPrism)
// Decode: validates the refinement condition
result := positiveIntCodec.Decode(42) // Success: Right(42)
result = positiveIntCodec.Decode(-5) // Failure: validation error
result = positiveIntCodec.Decode(0) // Failure: validation error
// Encode: converts back to base type
encoded := positiveIntCodec.Encode(42) // Returns: 42
Use cases:
- Creating codecs for refined types (positive numbers, non-empty strings, etc.)
- Validating that values meet specific constraints
- Building type-safe APIs with refined types
- Composing refinements with other codecs using Pipe
Common refinement patterns:
- Numeric constraints: PositiveInt, NonNegativeFloat, BoundedInt
- String constraints: NonEmptyString, EmailAddress, URL
- Collection constraints: NonEmptyArray, UniqueElements
- Domain-specific constraints: ValidAge, ValidZipCode, ValidCreditCard
Note: The refinement's GetOption returning None will result in a validation error with a message indicating the type cannot be refined. For more specific error messages, consider using MakeType directly with custom validation logic.
func Id ¶ added in v2.1.19
func Id[T any]() Type[T, T, T]
Id creates an identity Type codec that performs no transformation or validation.
An identity codec is a Type[T, T, T] where:
- Decode: Always succeeds and returns the input value unchanged
- Encode: Returns the input value unchanged (identity function)
- Validation: Always succeeds without any checks
This is useful as:
- A building block for more complex codecs
- A no-op codec when you need a Type but don't want any transformation
- A starting point for codec composition
- Testing and debugging codec pipelines
Type Parameters:
- T: The type that passes through unchanged
Returns:
- A Type[T, T, T] that performs identity operations on type T
The codec:
- Name: Uses the type's string representation (e.g., "int", "string")
- Is: Checks if a value is of type T
- Validate: Always succeeds and returns the input value
- Encode: Identity function (returns input unchanged)
Example:
// Create an identity codec for strings
stringId := codec.Id[string]()
// Decode always succeeds
result := stringId.Decode("hello") // Success: Right("hello")
// Encode is identity
encoded := stringId.Encode("world") // Returns: "world"
// Use in composition
arrayOfStrings := codec.TranscodeArray(stringId)
result := arrayOfStrings.Decode([]string{"a", "b", "c"})
Use cases:
- When you need a Type but don't want any validation or transformation
- As a placeholder in generic code that requires a Type parameter
- Building blocks for TranscodeArray, TranscodeEither, etc.
- Testing codec composition without side effects
Note: Unlike MakeSimpleType which validates the type, Id always succeeds in validation. It only checks the type during the Is operation.
func Int ¶
Int creates a Type for int values. It validates that input is an int type and provides identity encoding/decoding. This is a simple type that accepts any input and validates it's an int.
Returns:
- A Type[int, int, any] that can validate, decode, and encode int values
Example:
intType := codec.Int()
result := intType.Decode(42) // Success: Right(42)
result := intType.Decode("42") // Failure: Left(validation errors)
encoded := intType.Encode(100) // Returns: 100
func Int64FromString ¶ added in v2.2.1
Int64FromString creates a bidirectional codec for parsing 64-bit integers from strings.
The codec decodes by calling strconv.ParseInt(s, 10, 64) and encodes by calling strconv.FormatInt. Only base-10 integers are accepted. Values outside the int64 range (-9223372036854775808 to 9223372036854775807) are rejected.
Returns:
- A Type[int64, string, string] codec
See Also:
- IntFromString: platform-width int variant
Example ¶
c := Int64FromString() fmt.Println(c.Encode(9223372036854775807))
Output: 9223372036854775807
func IntFromString ¶ added in v2.2.1
IntFromString creates a bidirectional codec for parsing integers from strings.
The codec decodes by calling strconv.Atoi and encodes by calling strconv.Itoa. Only base-10 integers with an optional leading sign are accepted; hexadecimal, octal, and floating-point strings are rejected.
Returns:
- A Type[int, string, string] codec
See Also:
- Int64FromString: 64-bit variant with explicit range
Example ¶
c := IntFromString() fmt.Println(c.Encode(42)) fmt.Println(c.Encode(-7))
Output: 42 -7
func MakeSimpleType ¶
func MakeType ¶
func MakeType[A, O, I any]( name string, is Reader[any, Result[A]], validate Validate[I, A], encode Encode[A, O], ) Type[A, O, I]
MakeType creates a new Type with the given name, type checker, validator, and encoder.
Parameters:
- name: A descriptive name for this type (used in error messages)
- is: A function that checks if a value is of type A
- validate: A function that validates and decodes input I to type A
- encode: A function that encodes type A to output O
Returns a Type[A, O, I] that can both encode and decode values.
func MarshalJSON ¶ added in v2.2.33
MarshalJSON creates a bidirectional codec for types that implement json.Marshaler and json.Unmarshaler.
The codec decodes by calling dec.UnmarshalJSON(b) and encodes by calling enc.MarshalJSON(). Both enc and dec are caller-supplied instances; the caller is responsible for ensuring they share the same underlying state when a round-trip is required.
Note: The codec name is "UnmarshalJSON" to reflect the primary decode operation.
Type Parameters:
- T: The Go type to encode/decode
Parameters:
- enc: A json.Marshaler used to encode values to JSON []byte
- dec: A json.Unmarshaler used to decode JSON []byte into type T
Returns:
- A Type[T, []byte, []byte] codec
func MarshalText ¶ added in v2.2.33
func MarshalText[T any]( enc encoding.TextMarshaler, dec encoding.TextUnmarshaler, ) Type[T, []byte, []byte]
MarshalText creates a bidirectional codec for types that implement encoding.TextMarshaler and encoding.TextUnmarshaler.
The codec decodes by calling dec.UnmarshalText(b) and encodes by calling enc.MarshalText(). Both enc and dec are caller-supplied instances; the caller is responsible for ensuring they share the same underlying state when a round-trip is required.
Note: The codec name is "UnmarshalText" to reflect the primary decode operation.
Type Parameters:
- T: The Go type to encode/decode
Parameters:
- enc: An encoding.TextMarshaler used to encode values to []byte
- dec: An encoding.TextUnmarshaler used to decode []byte into type T
Returns:
- A Type[T, []byte, []byte] codec
func MonadAlt ¶ added in v2.2.12
func MonadAlt[A, O, I any](first Type[A, O, I], second Lazy[Type[A, O, I]]) Type[A, O, I]
MonadAlt creates a new codec that tries the first codec, and if it fails during validation, tries the second codec as a fallback.
This function implements the Alternative typeclass pattern for codecs, enabling "try this codec, or else try that codec" logic. It's particularly useful for:
- Handling multiple valid input formats
- Providing backward compatibility with legacy formats
- Implementing graceful degradation in parsing
- Supporting union types or polymorphic data
The resulting codec uses the first codec's encoder and combines both validators using the Alternative pattern. If both validations fail, errors from both are aggregated for comprehensive error reporting.
Type Parameters ¶
- A: The target type that both codecs decode to
- O: The output type that both codecs encode to
- I: The input type that both codecs decode from
Parameters ¶
- first: The primary codec to try first. Its encoder is used for the result.
- second: A lazy codec that serves as the fallback. It's only evaluated if the first validation fails.
Returns ¶
A new Type[A, O, I] that combines both codecs with Alternative semantics.
Behavior ¶
**Validation**:
- **First succeeds**: Returns the first result, second is never evaluated
- **First fails, second succeeds**: Returns the second result
- **Both fail**: Aggregates errors from both validators
**Encoding**:
- Always uses the first codec's encoder
- This assumes both codecs encode to the same output format
**Type Checking**:
- Uses the generic Is[A]() type checker
- Validates that values are of type A
Example: Multiple Input Formats ¶
import (
"github.com/IBM/fp-go/v2/optics/codec"
)
// Accept integers as either strings or numbers
intFromString := codec.IntFromString()
intFromNumber := codec.Int()
// Try parsing as string first, fall back to number
flexibleInt := codec.MonadAlt(
intFromString,
func() codec.Type[int, any, any] { return intFromNumber },
)
// Can now decode both "42" and 42
result1 := flexibleInt.Decode("42") // Success(42)
result2 := flexibleInt.Decode(42) // Success(42)
Example: Backward Compatibility ¶
// Support both old and new configuration formats
newConfigCodec := codec.Struct(/* new format */)
oldConfigCodec := codec.Struct(/* old format */)
// Try new format first, fall back to old format
configCodec := codec.MonadAlt(
newConfigCodec,
func() codec.Type[Config, any, any] { return oldConfigCodec },
)
// Automatically handles both formats
config := configCodec.Decode(input)
Example: Error Aggregation ¶
// Both validations will fail for invalid input
result := flexibleInt.Decode("not a number")
// Result contains errors from both string and number parsing attempts
Notes ¶
- The second codec is lazily evaluated for efficiency
- First success short-circuits evaluation (second not called)
- Errors are aggregated when both fail
- The resulting codec's name is "Alt[<first codec name>]"
- Both codecs must have compatible input and output types
- The first codec's encoder is always used
See Also ¶
- Alt: The curried, point-free version
- validate.MonadAlt: The underlying validation operation
- Either: For codecs that decode to Either[L, R] types
func Nil ¶
MakeNilType creates a Type that validates nil values. It accepts any input and validates that it is nil, returning a typed nil pointer.
Example:
nilType := codec.MakeNilType[string]()
result := nilType.Decode(nil) // Success: Right((*string)(nil))
result := nilType.Decode("not nil") // Failure: Left(errors)
func NonEmptyString ¶ added in v2.2.70
NonEmptyString creates a bidirectional codec for non-empty strings.
The codec decodes by asserting the input string is not empty and encodes by returning the string unchanged. A string containing only whitespace passes validation; only the empty string "" is rejected.
This is a specialised version of FromNonZero[string]() that carries the name "NonEmptyString" for clearer error messages.
Returns:
- A Type[string, string, string] codec
See Also:
- FromNonZero: general non-zero codec for any comparable type
Example ¶
c := NonEmptyString()
fmt.Println(c.Encode("hello"))
Output: hello
func Regex ¶ added in v2.2.1
Regex creates a bidirectional codec for regex pattern matching with capture groups.
The codec decodes by attempting to match the compiled regular expression against the input string. On success it returns a prism.Match containing:
- Before: text before the match
- Groups: capture groups (index 0 is the full match, 1+ are numbered groups)
- After: text after the match
Encoding reconstructs the original string from a prism.Match value. Decoding fails if the regex does not match.
Parameters:
- re: A compiled regular expression
Returns:
- A Type[prism.Match, string, string] codec
See Also:
- RegexNamed: variant that exposes named capture groups as a map
Example ¶
c := Regex(regexp.MustCompile(`\d+`))
m := prism.Match{Before: "Price: ", Groups: []string{"42"}, After: " dollars"}
fmt.Println(c.Encode(m))
Output: Price: 42 dollars
func RegexNamed ¶ added in v2.2.1
RegexNamed creates a bidirectional codec for regex pattern matching with named capture groups.
The codec decodes by attempting to match the compiled regular expression against the input string. On success it returns a prism.NamedMatch containing:
- Before: text before the match
- Groups: map of named capture groups (name → matched text)
- Full: the complete matched text
- After: text after the match
Encoding reconstructs the original string from a prism.NamedMatch value. Decoding fails if the regex does not match.
Parameters:
- re: A compiled regular expression with named capture groups (e.g., `(?P<name>pattern)`)
Returns:
- A Type[prism.NamedMatch, string, string] codec
See Also:
- Regex: variant that exposes capture groups as an ordered slice
Example ¶
c := RegexNamed(regexp.MustCompile(`(?P<user>\w+)@(?P<domain>\w+\.\w+)`))
m := prism.NamedMatch{Before: "", Full: "john@example.com", After: ""}
fmt.Println(c.Encode(m))
Output: john@example.com
func Stat ¶ added in v2.3.72
func Stat() Type[FileInfoWithPath, string, string]
Stat creates a bidirectional codec for file-system path resolution and stat retrieval.
The codec resolves the input string to an absolute path via filepath.Abs, calls os.Stat on it, and wraps the resulting fs.FileInfo together with the resolved absolute path in a FileInfoWithPath value.
Encoding converts a FileInfoWithPath back to its absolute path string via AbsPath.
Decoding fails if filepath.Abs fails or if the path does not exist or is not accessible (i.e. os.Stat returns an error).
Returns:
- A Type[FileInfoWithPath, string, string] codec
See Also:
- FileInfoWithPath: the decoded value type
func String ¶
String creates a Type for string values. It validates that input is a string type and provides identity encoding/decoding. This is a simple type that accepts any input and validates it's a string.
Returns:
- A Type[string, string, any] that can validate, decode, and encode string values
Example:
stringType := codec.String()
result := stringType.Decode("hello") // Success: Right("hello")
result := stringType.Decode(123) // Failure: Left(validation errors)
encoded := stringType.Encode("world") // Returns: "world"
func TranscodeArray ¶ added in v2.1.19
func TranscodeArray[T, O, I any](item Type[T, O, I]) Type[[]T, []O, []I]
TranscodeArray creates a Type for array/slice values with strongly-typed input. Unlike Array which accepts any input type, TranscodeArray requires the input to be a slice of type []I, providing type safety at the input level.
This function validates each element of the input slice using the provided item Type, transforming []I -> []T during decoding and []T -> []O during encoding.
Type Parameters:
- T: The type of elements in the decoded array
- O: The type of elements in the encoded array
- I: The type of elements in the input array (must be a slice)
Parameters:
- item: A Type[T, O, I] that defines how to validate/encode individual elements
Returns:
- A Type[[]T, []O, []I] that can validate, decode, and encode array values
The function:
- Requires input to be exactly []I (not any)
- Validates each element using the item Type's validation logic
- Collects all validation errors from individual elements
- Provides detailed context for each element's position in error messages
- Maps the encode function over all elements during encoding
Example:
// Create a codec that transforms string slices to int slices
stringToInt := codec.MakeType[int, int, string](
"StringToInt",
func(s any) result.Result[int] { ... },
func(s string) codec.Validate[int] { ... },
func(i int) int { return i },
)
arrayCodec := codec.TranscodeArray(stringToInt)
// Decode: []string -> []int
result := arrayCodec.Decode([]string{"1", "2", "3"}) // Success: Right([1, 2, 3])
result := arrayCodec.Decode([]string{"1", "x", "3"}) // Failure: validation error at index 1
// Encode: []int -> []int
encoded := arrayCodec.Encode([]int{1, 2, 3}) // Returns: []int{1, 2, 3}
Use TranscodeArray when:
- You need type-safe input validation ([]I instead of any)
- You're transforming between different slice element types
- You want compile-time guarantees about input types
Use Array when:
- You need to accept various input types (any, reflect.Value, etc.)
- You're working with dynamic or unknown input types
func TranscodeEither ¶ added in v2.1.19
func TranscodeEither[L, R, OL, OR, IL, IR any](leftItem Type[L, OL, IL], rightItem Type[R, OR, IR]) Type[either.Either[L, R], either.Either[OL, OR], either.Either[IL, IR]]
TranscodeEither creates a Type for Either values with strongly-typed left and right branches. It validates and transforms Either[IL, IR] to Either[L, R] during decoding, and Either[L, R] to Either[OL, OR] during encoding.
This function is useful for handling sum types (discriminated unions) where a value can be one of two possible types. Each branch (Left and Right) is validated and transformed independently using its respective Type codec.
Type Parameters:
- L: The type of the decoded Left value
- R: The type of the decoded Right value
- OL: The type of the encoded Left value
- OR: The type of the encoded Right value
- IL: The type of the input Left value
- IR: The type of the input Right value
Parameters:
- leftItem: A Type[L, OL, IL] that defines how to validate/encode Left values
- rightItem: A Type[R, OR, IR] that defines how to validate/encode Right values
Returns:
- A Type[Either[L, R], Either[OL, OR], Either[IL, IR]] that can validate, decode, and encode Either values
The function:
- Validates Left values using leftItem's validation logic
- Validates Right values using rightItem's validation logic
- Preserves the Either structure (Left stays Left, Right stays Right)
- Provides context-aware error messages indicating which branch failed
- Transforms values through the respective codecs during encoding
Example:
// Create a codec for Either[string, int]
stringCodec := codec.String()
intCodec := codec.Int()
eitherCodec := codec.TranscodeEither(stringCodec, intCodec)
// Decode Left value
leftResult := eitherCodec.Decode(either.Left[int]("error"))
// Success: Right(Either.Left("error"))
// Decode Right value
rightResult := eitherCodec.Decode(either.Right[string](42))
// Success: Right(Either.Right(42))
// Encode Left value
encodedLeft := eitherCodec.Encode(either.Left[int]("error"))
// Returns: Either.Left("error")
// Encode Right value
encodedRight := eitherCodec.Encode(either.Right[string](42))
// Returns: Either.Right(42)
Use TranscodeEither when:
- You need to handle sum types or discriminated unions
- You want to validate and transform both branches of an Either independently
- You're working with error handling patterns (Left for errors, Right for success)
- You need type-safe transformations for both possible values
Common patterns:
- Error handling: Either[Error, Value]
- Optional with reason: Either[Reason, Value]
- Validation results: Either[ValidationError, ValidatedData]
func URL ¶ added in v2.2.1
URL creates a bidirectional codec for URL parsing and formatting.
The codec decodes by calling url.Parse on the input string and validates URL syntax. Encoding converts a *url.URL back to its string representation via (*url.URL).String.
Returns:
- A Type[*url.URL, string, string] codec
Example ¶
c := URL()
u, _ := url.Parse("https://example.com/path")
fmt.Println(c.Encode(u))
Output: https://example.com/path
type Validate ¶
Validate is a function that validates input I to produce type A. It takes an input and returns a Reader that depends on the validation Context.
The Validate type is the core validation abstraction, defined as:
Reader[I, Decode[Context, A]]
This means:
- It takes an input of type I
- Returns a Reader that depends on validation Context
- That Reader produces a Validation[A] (Either[Errors, A])
This layered structure allows validators to:
- Access the input value
- Track validation context (path in nested structures)
- Accumulate multiple validation errors
- Compose with other validators
Example:
A Validate[string, int] takes a string and returns a context-aware function that validates and converts it to an integer.
type Validation ¶
type Validation[A any] = validation.Validation[A]
Validation represents the result of a validation operation that may contain validation errors or a successfully validated value of type A.
type Void ¶ added in v2.2.29
Void represents a unit type with a single value.
Void is used instead of struct{} to represent:
- Unit values in functional programming
- Placeholder types where no meaningful value is needed
- Return types for functions that produce no useful result
The single value of type Void is VOID (function.VOID).
Usage:
- Use function.Void (or F.Void) as the type
- Use function.VOID (or F.VOID) as the value
Example:
unitCodec := codec.Empty[F.Void, F.Void, any](
lazy.Of(pair.MakePair(F.VOID, F.VOID)),
)
Benefits over struct{}:
- More explicit intent (unit type vs empty struct)
- Consistent with functional programming conventions
- Better semantic meaning in type signatures
See also:
- function.VOID: The single value of type Void
- Empty: Codec function that uses Void for unit types
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package validate provides functional validation primitives for building composable validators.
|
Package validate provides functional validation primitives for building composable validators. |
|
Package validation provides functional validation types and operations for the codec system.
|
Package validation provides functional validation types and operations for the codec system. |