validate

package
v2.2.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 2, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package validate provides functional validation primitives for building composable validators.

This package implements a validation framework based on functional programming principles, allowing you to build complex validators from simple, composable pieces. It uses the Reader monad pattern to thread validation context through nested structures.

Core Concepts

The validate package is built around several key types:

  • Validate[I, A]: A validator that transforms input I to output A with validation context
  • Validation[A]: The result of validation, either errors or a valid value A
  • Context: Tracks the path through nested structures for detailed error messages

Type Structure

A Validate[I, A] is defined as:

Reader[I, Decode[A]]]

This means:

  1. It takes an input of type I
  2. Returns a Reader that depends on validation Context
  3. 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

Validation Context

The Context type tracks the path through nested data structures during validation. Each ContextEntry contains:

  • Key: The field name or map key
  • Type: The expected type name
  • Actual: The actual value being validated

This provides detailed error messages like "at user.address.zipCode: expected string, got number".

Monoid Operations

The package provides ApplicativeMonoid for combining validators using monoid operations. This allows you to:

  • Combine multiple validators that produce monoidal values
  • Accumulate results from parallel validations
  • Build complex validators from simpler ones

Example Usage

Basic validation structure:

import (
    "github.com/IBM/fp-go/v2/optics/codec/validate"
    "github.com/IBM/fp-go/v2/optics/codec/validation"
)

// A validator that checks if a string is non-empty
func nonEmptyString(input string) validate.Reader[validation.Context, validation.Validation[string]] {
    if input == "" {
        return validation.FailureWithMessage[string](input, "string must not be empty")
    }
    return func(ctx validation.Context) validation.Validation[string] {
        return validation.Success(input)
    }
}

// Create a Validate function
var validateNonEmpty validate.Validate[string, string] = func(input string) validate.Reader[validation.Context, validation.Validation[string]] {
    return nonEmptyString(input)
}

Combining validators with monoids:

import (
    "github.com/IBM/fp-go/v2/monoid"
    "github.com/IBM/fp-go/v2/string"
)

// Combine string validators using string concatenation monoid
stringMonoid := string.Monoid
validatorMonoid := validate.ApplicativeMonoid[string, string](stringMonoid)

// Now you can combine validators that produce strings
combined := validatorMonoid.Concat(validator1, validator2)

Integration with Codec

This package is designed to work with the optics/codec package for building type-safe encoders and decoders with validation. Validators can be composed into codecs that handle serialization, deserialization, and validation in a unified way.

Error Handling

Validation errors are accumulated using the Either monad's applicative instance. This means:

  • Multiple validation errors can be collected in a single pass
  • Errors include full context path for debugging
  • Errors can be formatted for logging or user display

See the validation package for error types and formatting options.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Context

type Context = validation.Context

Context provides contextual information for validation operations, tracking the path through nested data structures.

Context is a slice of ContextEntry values, where each entry represents a level in the nested structure being validated. This enables detailed error messages that show exactly where validation failed.

Example context path for nested validation:

Context{
  {Key: "user", Type: "User"},
  {Key: "address", Type: "Address"},
  {Key: "zipCode", Type: "string"},
}
// Represents: user.address.zipCode

The context is used to generate error messages like:

"at user.address.zipCode: expected string, got number"

type Decode

type Decode[I, A any] = decode.Decode[I, A]

Decode represents a decoding operation that transforms input I into output A within a validation context.

Type structure:

Decode[I, A] = Reader[Context, Validation[A]]

This means:

  1. Takes a validation Context (path through nested structures)
  2. Returns a Validation[A] (Either[Errors, A])

Decode is used as the foundation for validation operations, providing:

  • Context-aware error reporting with detailed paths
  • Error accumulation across multiple validations
  • Composable validation logic

The Decode type is typically not used directly but through the Validate type, which adds an additional Reader layer for accessing the input value.

Example:

decoder := func(ctx Context) Validation[int] {
  // Perform validation and return result
  return validation.Success(42)
}
// decoder is a Decode[any, int]

type Endomorphism added in v2.2.6

type Endomorphism[A any] = endomorphism.Endomorphism[A]

Endomorphism represents a function from a type to itself.

Type: Endomorphism[A] = func(A) A

An endomorphism is a morphism (structure-preserving map) where the source and target are the same type. In simpler terms, it's a function that takes a value of type A and returns a value of the same type A.

Endomorphisms are useful for:

  • Transformations that preserve type (e.g., string normalization)
  • Composable updates and modifications
  • Building pipelines of same-type transformations
  • Implementing the Monoid pattern (composition as binary operation)

Endomorphisms form a Monoid under function composition:

  • Identity: func(a A) A { return a }
  • Concat: func(f, g Endomorphism[A]) Endomorphism[A] { return func(a A) A { return f(g(a)) } }

Example:

trim := strings.TrimSpace      // Endomorphism[string]
lower := strings.ToLower       // Endomorphism[string]
normalize := compose(trim, lower)  // Endomorphism[string]

type Errors

type Errors = validation.Errors

Errors is a collection of validation errors that occurred during validation.

Each error in the collection contains:

  • The value that failed validation
  • The context path where the error occurred
  • A human-readable error message
  • An optional underlying cause error

Errors can be accumulated from multiple validation failures, allowing all problems to be reported at once rather than failing fast.

type Kleisli

type Kleisli[I, A, B any] = Reader[A, Validate[I, B]]

Kleisli represents a Kleisli arrow for the Validate monad.

A Kleisli arrow is a function from A to a monadic value Validate[I, B]. It's used for composing computations that produce monadic results.

Type: Kleisli[I, A, B] = func(A) Validate[I, B]

Kleisli arrows can be composed using the Chain function, enabling sequential validation where later validators depend on earlier results.

Example:

parseString := func(s string) Validate[string, int] {
  // Parse string to int with validation
}
checkPositive := func(n int) Validate[string, int] {
  // Validate that int is positive
}
// Both are Kleisli arrows that can be composed

type Monoid

type Monoid[A any] = monoid.Monoid[A]

Monoid represents an algebraic structure with an associative binary operation and an identity element. Used for combining values of type A.

A Monoid[A] must satisfy:

  • Associativity: Concat(Concat(a, b), c) == Concat(a, Concat(b, c))
  • Identity: Concat(Empty(), a) == a == Concat(a, Empty())

Common examples:

  • Numbers with addition (identity: 0)
  • Numbers with multiplication (identity: 1)
  • Strings with concatenation (identity: "")
  • Lists with concatenation (identity: [])

func ApplicativeMonoid

func ApplicativeMonoid[I, A any](m Monoid[A]) Monoid[Validate[I, A]]

ApplicativeMonoid creates a Monoid instance for Validate[I, A] given a Monoid[A].

This function lifts a monoid operation on values of type A to work with validators that produce values of type A. It uses the applicative functor structure of the nested Reader types to combine validators while preserving their validation context.

The resulting monoid allows you to:

  • Combine multiple validators that produce monoidal values
  • Run validators in parallel and merge their results using the monoid operation
  • Build complex validators compositionally from simpler ones

Type Parameters

  • I: The input type that validators accept
  • A: The output type that validators produce (must have a Monoid instance)

Parameters

  • m: A Monoid[A] that defines how to combine values of type A

Returns

A Monoid[Validate[I, A]] that can combine validators using the applicative structure.

How It Works

The function composes three layers of applicative monoids:

  1. The innermost layer uses validation.ApplicativeMonoid(m) to combine Validation[A] values
  2. The middle layer wraps this in reader.ApplicativeMonoid for the Context dependency
  3. The outer layer wraps everything in reader.ApplicativeMonoid for the input I dependency

This creates a monoid that:

  • Takes the same input I for both validators
  • Threads the same Context through both validators
  • Combines successful results using the monoid operation on A
  • Accumulates validation errors from both validators if either fails

Example

Combining string validators using string concatenation:

import (
    "github.com/IBM/fp-go/v2/monoid"
    "github.com/IBM/fp-go/v2/string"
    "github.com/IBM/fp-go/v2/optics/codec/validate"
    "github.com/IBM/fp-go/v2/optics/codec/validation"
)

// Create a monoid for string validators
stringMonoid := string.Monoid
validatorMonoid := validate.ApplicativeMonoid[string, string](stringMonoid)

// Define two validators that extract different parts
validator1 := func(input string) validate.Reader[validation.Context, validation.Validation[string]] {
    return func(ctx validation.Context) validation.Validation[string] {
        return validation.Success("Hello ")
    }
}

validator2 := func(input string) validate.Reader[validation.Context, validation.Validation[string]] {
    return func(ctx validation.Context) validation.Validation[string] {
        return validation.Success("World")
    }
}

// Combine them - results will be concatenated
combined := validatorMonoid.Concat(validator1, validator2)
// When run, produces validation.Success("Hello World")

Combining numeric validators using addition:

import (
    "github.com/IBM/fp-go/v2/number"
)

// Create a monoid for int validators using addition
intMonoid := number.MonoidSum[int]()
validatorMonoid := validate.ApplicativeMonoid[string, int](intMonoid)

// Validators that extract and validate different numeric fields
// Results will be summed together

Notes

  • Both validators receive the same input value I
  • If either validator fails, all errors are accumulated
  • If both succeed, their results are combined using the monoid operation
  • The empty element of the monoid serves as the identity for the Concat operation
  • This follows the applicative functor laws for combining effectful computations

See Also

  • validation.ApplicativeMonoid: The underlying monoid for validation results
  • reader.ApplicativeMonoid: The monoid for reader computations
  • Monoid[A]: The monoid instance for the result type

type Operator

type Operator[I, A, B any] = Kleisli[I, Validate[I, A], B]

Operator represents a transformation operator for validators.

An Operator transforms a Validate[I, A] into a Validate[I, B]. It's a specialized Kleisli arrow where the input is itself a validator.

Type: Operator[I, A, B] = func(Validate[I, A]) Validate[I, B]

Operators are used to:

  • Transform validation results (Map)
  • Chain dependent validations (Chain)
  • Apply function validators to value validators (Ap)

Example:

toUpper := Map[string, string, string](strings.ToUpper)
// toUpper is an Operator[string, string, string]
// It can be applied to any string validator to uppercase the result

func Ap

func Ap[B, I, A any](fa Validate[I, A]) Operator[I, func(A) B, B]

Ap creates an operator that applies a function validator to a value validator.

This is the curried version of MonadAp, returning a function that can be applied to function validators. It's useful for creating reusable applicative patterns.

Type Parameters

  • B: The result type after applying the function
  • I: The input type
  • A: The type of the value to which the function is applied

Parameters

  • fa: A validator that produces a value of type A

Returns

An Operator[I, func(A) B, B] that applies function validators to the value validator.

Example

// Create a value validator
validateValue := validate.Of[string, int](21)

// Create an applicative operator
applyTo21 := validate.Ap[int, string, int](validateValue)

// Create a function validator
validateDouble := validate.Of[string, func(int) int](func(x int) int { return x * 2 })

// Apply it
result := applyTo21(validateDouble)
// When run, produces validation.Success(42)

Notes

  • This is the point-free style version of MonadAp
  • Useful for building applicative pipelines
  • Enables parallel validation with error accumulation
  • Can be composed with other applicative operators

func ApS added in v2.2.6

func ApS[I, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa Validate[I, T],
) Operator[I, S1, S2]

ApS attaches a value to a context S1 to produce a context S2 by considering the context and the value concurrently. This uses the applicative functor pattern, allowing parallel composition.

IMPORTANT: Unlike Bind which fails fast, ApS aggregates ALL validation errors from both the context and the value. If both validations fail, all errors are collected and returned together. This is useful for validating multiple independent fields and reporting all errors at once.

Example:

type State struct { x int; y int }
decoder := F.Pipe2(
    Do[string](State{}),
    ApS(func(x int) func(State) State {
        return func(s State) State { s.x = x; return s }
    }, Of[string](42)),
)
result := decoder("input") // Returns validation.Success(State{x: 42})

Error aggregation example:

// Both decoders fail - errors are aggregated
decoder1 := func(input string) Validation[State] {
    return validation.Failures[State](/* errors */)
}
decoder2 := func(input string) Validation[int] {
    return validation.Failures[int](/* errors */)
}
combined := ApS(setter, decoder2)(decoder1)
result := combined("input") // Contains BOTH sets of errors

func ApSL added in v2.2.6

func ApSL[I, S, T any](
	lens L.Lens[S, T],
	fa Validate[I, T],
) Operator[I, S, S]

ApSL attaches a value to a context using a lens-based setter. This is a convenience function that combines ApS with a lens, allowing you to use optics to update nested structures in a more composable way.

IMPORTANT: Like ApS, this function aggregates ALL validation errors. If both the context and the value fail validation, all errors are collected and returned together. This enables comprehensive error reporting for complex nested structures.

The lens parameter provides both the getter and setter for a field within the structure S. This eliminates the need to manually write setter functions.

Example:

type Address struct {
    Street string
    City   string
}

type Person struct {
    Name    string
    Address Address
}

// Create a lens for the Address field
addressLens := lens.MakeLens(
    func(p Person) Address { return p.Address },
    func(p Person, a Address) Person { p.Address = a; return p },
)

// Use ApSL to update the address
decoder := F.Pipe2(
    Of[string](Person{Name: "Alice"}),
    ApSL(
        addressLens,
        Of[string](Address{Street: "Main St", City: "NYC"}),
    ),
)
result := decoder("input") // Returns validation.Success(Person{...})

func Bind added in v2.2.6

func Bind[I, S1, S2, A any](
	setter func(A) func(S1) S2,
	f Kleisli[I, S1, A],
) Operator[I, S1, S2]

Bind attaches the result of a computation to a context S1 to produce a context S2. This is used in do-notation style to sequentially build up a context.

Example:

type State struct { x int; y int }
decoder := F.Pipe2(
    Do[string](State{}),
    Bind(func(x int) func(State) State {
        return func(s State) State { s.x = x; return s }
    }, func(s State) Validate[string, int] {
        return Of[string](42)
    }),
)
result := decoder("input") // Returns validation.Success(State{x: 42})

func BindL added in v2.2.6

func BindL[I, S, T any](
	lens L.Lens[S, T],
	f Kleisli[I, T, T],
) Operator[I, S, S]

BindL attaches the result of a computation to a context using a lens-based setter. This is a convenience function that combines Bind with a lens, allowing you to use optics to update nested structures based on their current values.

The lens parameter provides both the getter and setter for a field within the structure S. The computation function f receives the current value of the focused field and returns a Validation that produces the new value.

Unlike ApSL, BindL uses monadic sequencing, meaning the computation f can depend on the current value of the focused field.

Example:

type Counter struct {
    Value int
}

valueLens := lens.MakeLens(
    func(c Counter) int { return c.Value },
    func(c Counter, v int) Counter { c.Value = v; return c },
)

// Increment the counter, but fail if it would exceed 100
increment := func(v int) Validate[string, int] {
    return func(input string) Validation[int] {
        if v >= 100 {
            return validation.Failures[int](/* errors */)
        }
        return validation.Success(v + 1)
    }
}

decoder := F.Pipe1(
    Of[string](Counter{Value: 42}),
    BindL(valueLens, increment),
)
result := decoder("input") // Returns validation.Success(Counter{Value: 43})

func BindTo added in v2.2.6

func BindTo[I, S1, T any](
	setter func(T) S1,
) Operator[I, T, S1]

BindTo initializes a new state S1 from a value T. This is typically used as the first operation after creating a Validate value.

Example:

type State struct { value int }
decoder := F.Pipe1(
    Of[string](42),
    BindTo[string](func(x int) State { return State{value: x} }),
)
result := decoder("input") // Returns validation.Success(State{value: 42})

func Chain

func Chain[I, A, B any](f Kleisli[I, A, B]) Operator[I, A, B]

Chain sequences two validators, where the second depends on the result of the first.

This is the monadic bind operation for Validate. It allows you to create validators that depend on the results of previous validations, enabling complex validation logic that builds on earlier results.

Type Parameters

  • I: The input type
  • A: The type of the first validation result
  • B: The type of the second validation result

Parameters

  • f: A Kleisli arrow that takes a value of type A and returns a Validate[I, B]

Returns

An Operator[I, A, B] that sequences the validations.

Example

// First validate that a string is non-empty, then validate its length
validateNonEmpty := func(s string) validate.Reader[validation.Context, validation.Validation[string]] {
    return func(ctx validation.Context) validation.Validation[string] {
        if s == "" {
            return validation.FailureWithMessage[string](s, "must not be empty")(ctx)
        }
        return validation.Success(s)
    }
}

validateLength := func(s string) validate.Validate[string, int] {
    return func(input string) validate.Reader[validation.Context, validation.Validation[int]] {
        return func(ctx validation.Context) validation.Validation[int] {
            if len(s) < 3 {
                return validation.FailureWithMessage[int](len(s), "too short")(ctx)
            }
            return validation.Success(len(s))
        }
    }
}

// Chain them together
chained := validate.Chain(validateLength)(validateNonEmpty)

Notes

  • If the first validation fails, the second is not executed
  • Errors from the first validation are preserved
  • This enables dependent validation logic
  • Satisfies the monad laws: associativity and identity

func Let added in v2.2.6

func Let[I, S1, S2, B any](
	key func(B) func(S1) S2,
	f func(S1) B,
) Operator[I, S1, S2]

Let attaches the result of a pure computation to a context S1 to produce a context S2. Unlike Bind, the computation function returns a plain value, not wrapped in Validate.

Example:

type State struct { x int; computed int }
decoder := F.Pipe2(
    Do[string](State{x: 5}),
    Let[string](func(c int) func(State) State {
        return func(s State) State { s.computed = c; return s }
    }, func(s State) int { return s.x * 2 }),
)
result := decoder("input") // Returns validation.Success(State{x: 5, computed: 10})

func LetL added in v2.2.6

func LetL[I, S, T any](
	lens L.Lens[S, T],
	f Endomorphism[T],
) Operator[I, S, S]

LetL attaches the result of a pure computation to a context using a lens-based setter. This is a convenience function that combines Let with a lens, allowing you to use optics to update nested structures with pure transformations.

The lens parameter provides both the getter and setter for a field within the structure S. The transformation function f receives the current value of the focused field and returns the new value directly (not wrapped in Validation).

This is useful for pure transformations that cannot fail, such as mathematical operations, string manipulations, or other deterministic updates.

Example:

type Counter struct {
    Value int
}

valueLens := lens.MakeLens(
    func(c Counter) int { return c.Value },
    func(c Counter, v int) Counter { c.Value = v; return c },
)

// Double the counter value
double := func(v int) int { return v * 2 }

decoder := F.Pipe1(
    Of[string](Counter{Value: 21}),
    LetL(valueLens, double),
)
result := decoder("input") // Returns validation.Success(Counter{Value: 42})

func LetTo added in v2.2.6

func LetTo[I, S1, S2, B any](
	key func(B) func(S1) S2,
	b B,
) Operator[I, S1, S2]

LetTo attaches a constant value to a context S1 to produce a context S2.

Example:

type State struct { x int; name string }
result := F.Pipe2(
    Do(State{x: 5}),
    LetTo(func(n string) func(State) State {
        return func(s State) State { s.name = n; return s }
    }, "example"),
)

func LetToL added in v2.2.6

func LetToL[I, S, T any](
	lens L.Lens[S, T],
	b T,
) Operator[I, S, S]

LetToL attaches a constant value to a context using a lens-based setter. This is a convenience function that combines LetTo with a lens, allowing you to use optics to set nested fields to specific values.

The lens parameter provides the setter for a field within the structure S. Unlike LetL which transforms the current value, LetToL simply replaces it with the provided constant value b.

This is useful for resetting fields, initializing values, or setting fields to predetermined constants.

Example:

type Config struct {
    Debug   bool
    Timeout int
}

debugLens := lens.MakeLens(
    func(c Config) bool { return c.Debug },
    func(c Config, d bool) Config { c.Debug = d; return c },
)

decoder := F.Pipe1(
    Of[string](Config{Debug: true, Timeout: 30}),
    LetToL(debugLens, false),
)
result := decoder("input") // Returns validation.Success(Config{Debug: false, Timeout: 30})

func Map

func Map[I, A, B any](f func(A) B) Operator[I, A, B]

Map creates an operator that transforms validation results.

This is the curried version of MonadMap, returning a function that can be applied to validators. It's useful for creating reusable transformation pipelines.

Type Parameters

  • I: The input type
  • A: The type of the current validation result
  • B: The type after applying the transformation

Parameters

  • f: The transformation function to apply to successful results

Returns

An Operator[I, A, B] that transforms Validate[I, A] to Validate[I, B].

Example

// Create a reusable transformation
toUpper := validate.Map[string, string, string](strings.ToUpper)

// Apply it to different validators
validator1 := toUpper(someStringValidator)
validator2 := toUpper(anotherStringValidator)

Notes

  • This is the point-free style version of MonadMap
  • Useful for building transformation pipelines
  • Can be composed with other operators

type Reader

type Reader[R, A any] = reader.Reader[R, A]

Reader represents a computation that depends on an environment R and produces a value A.

Reader[R, A] is a function type: func(R) A

The Reader pattern is used to:

  • Thread configuration or context through computations
  • Implement dependency injection in a functional way
  • Defer computation until the environment is available
  • Compose computations that share the same environment

Example:

type Config struct { Port int }
getPort := func(cfg Config) int { return cfg.Port }
// getPort is a Reader[Config, int]

type Validate

type Validate[I, A any] = Reader[I, Decode[Context, A]]

Validate represents a composable validator that transforms input I to output A with comprehensive error tracking and context propagation.

Type Structure

Validate[I, A] = Reader[I, Decode[Context, A]]
               = Reader[I, Reader[Context, Validation[A]]]
               = func(I) func(Context) Either[Errors, A]

This three-layer structure provides:

  1. Input access: The outer Reader[I, ...] gives access to the input value I
  2. Context tracking: The middle Reader[Context, ...] tracks the validation path
  3. Error handling: The inner Validation[A] accumulates errors or produces value A

Purpose

Validate is the core type for building type-safe, composable validators that:

  • Transform and validate data from one type to another
  • Track the path through nested structures for detailed error messages
  • Accumulate multiple validation errors instead of failing fast
  • Compose with other validators using functional patterns

Key Features

  • Context-aware: Automatically tracks validation path (e.g., "user.address.zipCode")
  • Error accumulation: Collects all validation errors, not just the first one
  • Type-safe: Leverages Go's type system to ensure correctness
  • Composable: Validators can be combined using Map, Chain, Ap, and other operators

Algebraic Structure

Validate forms several algebraic structures:

  • Functor: Transform successful results with Map
  • Applicative: Combine independent validators in parallel with Ap
  • Monad: Chain dependent validators sequentially with Chain

Example Usage

Basic validator:

validatePositive := func(n int) Reader[Context, Validation[int]] {
  return func(ctx Context) Validation[int] {
    if n > 0 {
      return validation.Success(n)
    }
    return validation.FailureWithMessage[int](n, "must be positive")(ctx)
  }
}
// validatePositive is a Validate[int, int]

Composing validators:

// Transform the result of a validator
doubled := Map[int, int, int](func(x int) int { return x * 2 })(validatePositive)

// Chain dependent validations
validateRange := func(n int) Validate[int, int] {
  return func(input int) Reader[Context, Validation[int]] {
    return func(ctx Context) Validation[int] {
      if n <= 100 {
        return validation.Success(n)
      }
      return validation.FailureWithMessage[int](n, "must be <= 100")(ctx)
    }
  }
}
combined := Chain(validateRange)(validatePositive)

Integration

Validate integrates with the broader optics/codec ecosystem:

  • Works with Decode for decoding operations
  • Uses Validation for error handling
  • Leverages Context for detailed error reporting
  • Composes with other codec types for complete encode/decode pipelines

See the package documentation for more examples and patterns.

func Do added in v2.2.6

func Do[I, S any](
	empty S,
) Validate[I, S]

Do creates an empty context of type S to be used with the Bind operation. This is the starting point for building up a context using do-notation style.

Example:

type Result struct {
    x int
    y string
}
result := Do(Result{})

func MonadAp

func MonadAp[B, I, A any](fab Validate[I, func(A) B], fa Validate[I, A]) Validate[I, B]

MonadAp applies a validator containing a function to a validator containing a value.

This is the applicative apply operation for Validate. It allows you to apply functions wrapped in validation context to values wrapped in validation context, accumulating errors from both if either fails.

Type Parameters

  • B: The result type after applying the function
  • I: The input type
  • A: The type of the value to which the function is applied

Parameters

  • fab: A validator that produces a function from A to B
  • fa: A validator that produces a value of type A

Returns

A Validate[I, B] that applies the function to the value if both validations succeed.

Example

// Create a validator that produces a function
validateFunc := validate.Of[string, func(int) int](func(x int) int { return x * 2 })

// Create a validator that produces a value
validateValue := validate.Of[string, int](21)

// Apply them
result := validate.MonadAp(validateFunc, validateValue)
// When run, produces validation.Success(42)

Notes

  • Both validators receive the same input
  • If either validation fails, all errors are accumulated
  • If both succeed, the function is applied to the value
  • This enables parallel validation with error accumulation
  • Satisfies the applicative functor laws

func MonadMap

func MonadMap[I, A, B any](fa Validate[I, A], f func(A) B) Validate[I, B]

MonadMap applies a function to the successful result of a validation.

This is the functor map operation for Validate. It transforms the success value without affecting the validation logic or error handling. If the validation fails, the function is not applied and errors are preserved.

Type Parameters

  • I: The input type
  • A: The type of the current validation result
  • B: The type after applying the transformation

Parameters

  • fa: The validator to transform
  • f: The transformation function to apply to successful results

Returns

A new Validate[I, B] that applies f to the result if validation succeeds.

Example

// Transform a string validator to uppercase
validateString := func(s string) validate.Reader[validation.Context, validation.Validation[string]] {
    return func(ctx validation.Context) validation.Validation[string] {
        return validation.Success(s)
    }
}

upperValidator := validate.MonadMap(validateString, strings.ToUpper)
result := upperValidator("hello")(nil)
// result is validation.Success("HELLO")

Notes

  • Preserves validation errors unchanged
  • Only applies the function to successful validations
  • Satisfies the functor laws: composition and identity

func Of

func Of[I, A any](a A) Validate[I, A]

Of creates a Validate that always succeeds with the given value.

This is the "pure" or "return" operation for the Validate monad. It lifts a plain value into the validation context without performing any actual validation.

Type Parameters

  • I: The input type (not used, but required for type consistency)
  • A: The type of the value to wrap

Parameters

  • a: The value to wrap in a successful validation

Returns

A Validate[I, A] that ignores its input and always returns a successful validation containing the value a.

Example

// Create a validator that always succeeds with value 42
alwaysValid := validate.Of[string, int](42)
result := alwaysValid("any input")(nil)
// result is validation.Success(42)

Notes

  • This is useful for lifting pure values into the validation context
  • The input type I is ignored; the validator succeeds regardless of input
  • This satisfies the monad laws: Of is the left and right identity for Chain

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.

Validation[A] is an Either[Errors, A], where:

  • Left(errors): Validation failed with one or more errors
  • Right(value): Validation succeeded with value of type A

The Validation type supports:

  • Error accumulation: Multiple validation errors can be collected
  • Applicative composition: Parallel validations with error aggregation
  • Monadic composition: Sequential validations with short-circuiting

Example:

success := validation.Success(42)           // Right(42)
failure := validation.Failure[int](errors)  // Left(errors)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL