Documentation
¶
Overview ¶
Package validation provides functional validation types and operations for the codec system.
This package implements a validation monad that accumulates errors during validation operations, making it ideal for form validation, data parsing, and other scenarios where you want to collect all validation errors rather than failing on the first error.
Core Concepts ¶
Validation[A]: Represents the result of a validation operation as Either[Errors, A]:
- Left(Errors): Validation failed with one or more errors
- Right(A): Successfully validated value of type A
ValidationError: A detailed error type that includes:
- Value: The actual value that failed validation
- Context: The path through nested structures (e.g., "user.address.zipCode")
- Message: Human-readable error description
- Cause: Optional underlying error
Context: A stack of ContextEntry values that tracks the validation path through nested data structures, enabling precise error reporting.
Basic Usage ¶
Creating validation results:
// Success case
valid := validation.Success(42)
// Failure case
invalid := validation.Failures[int](validation.Errors{
&validation.ValidationError{
Value: "not a number",
Message: "expected integer",
Context: nil,
},
})
Using with context:
failWithMsg := validation.FailureWithMessage[int]("invalid", "must be positive")
result := failWithMsg([]validation.ContextEntry{
{Key: "age", Type: "int"},
})
Applicative Validation ¶
The validation type supports applicative operations, allowing you to combine multiple validations and accumulate all errors:
type User struct {
Name string
Email string
Age int
}
validateName := func(s string) validation.Validation[string] {
if len(s) > 0 {
return validation.Success(s)
}
return validation.Failures[string](/* error */)
}
// Combine validations - all errors will be collected
result := validation.Ap(validation.Ap(validation.Ap(
validation.Of(func(name string) func(email string) func(age int) User {
return func(email string) func(age int) User {
return func(age int) User {
return User{name, email, age}
}
}
}),
)(validateName("")))(validateEmail("")))(validateAge(-1))
Error Formatting ¶
ValidationError implements custom formatting for detailed error messages:
err := &ValidationError{
Value: "abc",
Context: []ContextEntry{{Key: "user"}, {Key: "age"}},
Message: "expected integer",
}
fmt.Printf("%v", err) // at user.age: expected integer
fmt.Printf("%+v", err) // at user.age: expected integer
// value: "abc"
Monoid Operations ¶
The package provides monoid instances for combining validations:
// Combine validation results
m := validation.ApplicativeMonoid(stringMonoid)
combined := m.Concat(validation.Success("hello"), validation.Success(" world"))
// Result: Success("hello world")
Integration ¶
This package integrates with:
- either: Validation is built on Either for error handling
- array: For collecting multiple errors
- monoid: For combining validation results
- reader: For context-dependent validation operations
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Applicative ¶
func Applicative[A, B any]() applicative.Applicative[A, B, Validation[A], Validation[B], Validation[func(A) B]]
Applicative creates an Applicative instance for Validation with error accumulation.
This returns a lawful Applicative that accumulates validation errors using the Errors monoid. Unlike the standard Either applicative which fails fast, this validation applicative collects all errors when combining independent validations with Ap.
The returned instance satisfies all applicative laws:
- Identity: Ap(Of(identity))(v) == v
- Homomorphism: Ap(Of(f))(Of(x)) == Of(f(x))
- Interchange: Ap(Of(f))(u) == Ap(Map(f => f(y))(u))(Of(y))
- Composition: Ap(Ap(Map(compose)(f))(g))(x) == Ap(f)(Ap(g)(x))
Key behaviors:
- Of: lifts a value into a successful Validation (Right)
- Map: transforms successful values, preserves failures (standard functor)
- Ap: when both operands fail, combines all errors using the Errors monoid
This is particularly useful for form validation, configuration validation, and any scenario where you want to collect all validation errors at once rather than stopping at the first failure.
Example - Validating Multiple Fields:
app := Applicative[string, User]()
// Validate individual fields
validateName := func(name string) Validation[string] {
if len(name) < 3 {
return Failure("Name must be at least 3 characters")
}
return Success(name)
}
validateAge := func(age int) Validation[int] {
if age < 18 {
return Failure("Must be 18 or older")
}
return Success(age)
}
// Create a curried constructor
makeUser := func(name string) func(int) User {
return func(age int) User {
return User{Name: name, Age: age}
}
}
// Combine validations - all errors are collected
name := validateName("ab") // Failure: name too short
age := validateAge(16) // Failure: age too low
result := app.Ap(age)(app.Ap(name)(app.Of(makeUser)))
// result contains both validation errors:
// - "Name must be at least 3 characters"
// - "Must be 18 or older"
Type Parameters:
- A: The input value type (Right value)
- B: The output value type after transformation
Returns:
An Applicative instance with Of, Map, and Ap operations that accumulate errors
Types ¶
type Context ¶
type Context = []ContextEntry
Context is a stack of ContextEntry values representing the path through nested structures during validation. Used to provide detailed error messages.
type ContextEntry ¶
type ContextEntry struct {
Key string // The key or field name (for objects/maps)
Type string // The expected type name
Actual any // The actual value being validated
}
ContextEntry represents a single entry in the validation context path. It tracks the location and type information during nested validation.
type Either ¶
Either represents a value that can be one of two types: Left (error) or Right (success).
type Kleisli ¶
type Kleisli[A, B any] = Reader[A, Validation[B]]
type Monoid ¶
func ApplicativeMonoid ¶
func ApplicativeMonoid[A any](m Monoid[A]) Monoid[Validation[A]]
ApplicativeMonoid creates a Monoid instance for Validation[A] given a Monoid for A. This allows combining validation results where the success values are also combined using the provided monoid. If any validation fails, all errors are accumulated.
The resulting monoid:
- Empty: Returns a successful validation with the empty value from the inner monoid
- Concat: Combines two validations:
- Both success: Combines values using the inner monoid
- Any failure: Accumulates all errors
Example:
import "github.com/IBM/fp-go/v2/string"
// Create a monoid for validations of strings
m := ApplicativeMonoid(string.Monoid)
v1 := Success("Hello")
v2 := Success(" World")
combined := m.Concat(v1, v2) // Success("Hello World")
v3 := Failures[string](someErrors)
failed := m.Concat(v1, v3) // Failures with accumulated errors
func ErrorsMonoid ¶
func ErrorsMonoid() Monoid[Errors]
ErrorsMonoid returns a Monoid instance for Errors (array of ValidationError pointers). The monoid concatenates error arrays, with an empty array as the identity element. This is used internally by the applicative operations to accumulate validation errors.
Example:
m := ErrorsMonoid() combined := m.Concat(errors1, errors2) // Concatenates both error arrays empty := m.Empty() // Returns empty error array
type Operator ¶
type Operator[A, B any] = Kleisli[Validation[A], B]
func Ap ¶
func Ap[B, A any](fa Validation[A]) Operator[func(A) B, B]
Ap applies a validation containing a function to a validation containing a value. This is the applicative apply operation that accumulates errors from both validations. If either validation fails, all errors are collected. If both succeed, the function is applied.
This enables combining multiple validations while collecting all errors:
Example:
// Validate multiple fields and collect all errors
validateUser := Ap(Ap(Of(func(name string) func(age int) User {
return func(age int) User { return User{name, age} }
}))(validateName))(validateAge)
func Map ¶
func Map[A, B any](f func(A) B) Operator[A, B]
Map transforms the value inside a successful validation using the provided function. If the validation is a failure, the errors are preserved unchanged. This is the functor map operation for Validation.
Example:
doubled := Map(func(x int) int { return x * 2 })(Of(21))
// Result: Success(42)
type Reader ¶
Reader represents a computation that depends on an environment R and produces a value A.
func FailureWithError ¶
func FailureWithError[T any](value any, message string) Reader[error, Reader[Context, Validation[T]]]
FailureWithError creates a validation failure with a custom message and underlying cause. Returns a Reader that takes an error, then a Context, and produces a Validation[T] failure. This is useful for wrapping errors from other operations while maintaining validation context.
Example:
fail := FailureWithError[int]("abc", "parse failed")
result := fail(parseErr)([]ContextEntry{{Key: "count", Type: "int"}})
func FailureWithMessage ¶
FailureWithMessage creates a validation failure with a custom message. Returns a Reader that takes a Context and produces a Validation[T] failure. This is useful for creating context-aware validation errors.
Example:
fail := FailureWithMessage[int]("abc", "expected integer")
result := fail([]ContextEntry{{Key: "age", Type: "int"}})
type Validation ¶
Validation represents the result of a validation operation. Left contains validation errors, Right contains the successfully validated value.
func Failures ¶
func Failures[T any](err Errors) Validation[T]
Failures creates a validation failure from a collection of errors. Returns a Left Either containing the errors.
type ValidationError ¶
type ValidationError struct {
Value any // The value that failed validation
Context Context // The path to the value in nested structures
Messsage string // Human-readable error message
Cause error
}
ValidationError represents a single validation failure with context.
func (*ValidationError) Error ¶
func (v *ValidationError) Error() string
Error implements the error interface for ValidationError. Returns a generic error message.
func (*ValidationError) Format ¶
Format implements fmt.Formatter for custom formatting of ValidationError. It includes the context path, message, and optionally the cause error. Supports verbs: %s, %v, %+v (with additional details)