decode

package
v2.2.6 Latest Latest
Warning

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

Go to latest
Published: Jan 31, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Decode

type Decode[I, A any] = Reader[I, Validation[A]]

Decode is a function that decodes input I to type A with validation. It combines the Reader pattern (for accessing input) with Validation (for error handling).

Type: func(I) Validation[A]

A Decode function:

  1. Takes raw input of type I (e.g., JSON, string, bytes)
  2. Attempts to decode/parse it into type A
  3. Returns a Validation[A] with either: - Success(A): Successfully decoded value - Failures(Errors): Validation errors describing what went wrong

This type is the foundation of the decode package, enabling composable, type-safe decoding with comprehensive error reporting.

Example:

// Decode a string to an integer
decodeInt := func(input string) Validation[int] {
    n, err := strconv.Atoi(input)
    if err != nil {
        return validation.Failures[int](validation.Errors{
            &validation.ValidationError{
                Value:    input,
                Messsage: "not a valid integer",
                Cause:    err,
            },
        })
    }
    return validation.Success(n)
}  // Decode[string, int]

result := decodeInt("42")  // Success(42)
result := decodeInt("abc") // Failures([...])

func Do added in v2.2.6

func Do[I, S any](
	empty S,
) Decode[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 Decode[I, func(A) B], fa Decode[I, A]) Decode[I, B]

MonadAp applies a decoder containing a function to a decoder containing a value. This is the applicative apply operation that enables parallel composition of decoders.

Example:

decoderFn := decode.Of[string](func(n int) string {
    return fmt.Sprintf("Number: %d", n)
})
decoderVal := decode.Of[string](42)
result := decode.MonadAp(decoderFn, decoderVal)

func MonadChain

func MonadChain[I, A, B any](fa Decode[I, A], f Kleisli[I, A, B]) Decode[I, B]

MonadChain sequences two decode operations, passing the result of the first to the second. This is the monadic bind operation that enables sequential composition of decoders.

Example:

decoder1 := decode.Of[string](42)
decoder2 := decode.MonadChain(decoder1, func(n int) Decode[string, string] {
    return decode.Of[string](fmt.Sprintf("Number: %d", n))
})

func MonadMap

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

MonadMap transforms the decoded value using the provided function. This is the functor map operation that applies a transformation to successful decode results.

Example:

decoder := decode.Of[string](42)
mapped := decode.MonadMap(decoder, func(n int) string {
    return fmt.Sprintf("Number: %d", n)
})

func Of

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

Of creates a Decode that always succeeds with the given value. This is the pointed functor operation that lifts a pure value into the Decode context.

Example:

decoder := decode.Of[string](42)
result := decoder("any input") // Always returns validation.Success(42)

type Endomorphism added in v2.2.6

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

Endomorphism represents a function from a type to itself: func(A) A. This is an alias for endomorphism.Endomorphism[A].

In the decode context, endomorphisms are used with LetL to transform decoded values using pure functions that don't change the type.

Endomorphisms are useful for:

  • Normalizing data (e.g., trimming strings, rounding numbers)
  • Applying business rules (e.g., clamping values to ranges)
  • Data sanitization (e.g., removing special characters)

Example:

// Normalize a string by trimming and lowercasing
normalize := func(s string) string {
    return strings.ToLower(strings.TrimSpace(s))
}  // Endomorphism[string]

// Clamp an integer to a range
clamp := func(n int) int {
    if n < 0 { return 0 }
    if n > 100 { return 100 }
    return n
}  // Endomorphism[int]

// Use with LetL to transform decoded values
decoder := F.Pipe1(
    decodeString,
    LetL(nameLens, normalize),
)

type Kleisli

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

Kleisli represents a function from A to a decoded B given input type I. It's a Reader that takes an input A and produces a Decode[I, B] function. This enables composition of decoding operations in a functional style.

Type: func(A) Decode[I, B]

which expands to: func(A) func(I) Validation[B]

Kleisli arrows are the fundamental building blocks for composing decoders. They allow you to chain decoding operations where each step can:

  1. Depend on the result of the previous step (the A parameter)
  2. Access the original input (the I parameter via Decode)
  3. Fail with validation errors (via Validation[B])

This is particularly useful for:

  • Conditional decoding based on previously decoded values
  • Multi-stage decoding pipelines
  • Dependent field validation

Example:

// Decode a user, then decode their age based on their type
decodeAge := func(userType string) Decode[map[string]any, int] {
    return func(data map[string]any) Validation[int] {
        if userType == "admin" {
            // Admins must be 18+
            age := data["age"].(int)
            if age < 18 {
                return validation.Failures[int](/* error */)
            }
            return validation.Success(age)
        }
        // Regular users can be any age
        return validation.Success(data["age"].(int))
    }
}  // Kleisli[map[string]any, string, int]

// Use with Chain to compose decoders
decoder := F.Pipe2(
    decodeUserType,           // Decode[map[string]any, string]
    Chain(decodeAge),         // Chains with Kleisli
    Map(func(age int) User {  // Transform to final type
        return User{Age: age}
    }),
)

type Operator

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

Operator represents a decoding transformation that takes a decoded A and produces a decoded B. It's a specialized Kleisli arrow for composing decode operations where the input is already decoded. This allows chaining multiple decode transformations together.

Type: func(Decode[I, A]) Decode[I, B]

Operators are higher-order functions that transform one decoder into another. They are the result of partially applying functions like Map, Chain, and Ap, making them ideal for use in composition pipelines with F.Pipe.

Key characteristics:

  • Takes a Decode[I, A] as input
  • Returns a Decode[I, B] as output
  • Preserves the input type I (the raw data being decoded)
  • Transforms the output type from A to B

Common operators:

  • Map(f): Transforms successful decode results
  • Chain(f): Sequences dependent decode operations
  • Ap(fa): Applies function decoders to value decoders

Example:

// Create reusable operators
toString := Map(func(n int) string {
    return strconv.Itoa(n)
})  // Operator[string, int, string]

validatePositive := Chain(func(n int) Decode[string, int] {
    return func(input string) Validation[int] {
        if n <= 0 {
            return validation.Failures[int](/* error */)
        }
        return validation.Success(n)
    }
})  // Operator[string, int, int]

// Compose operators in a pipeline
decoder := F.Pipe2(
    decodeInt,          // Decode[string, int]
    validatePositive,   // Operator[string, int, int]
    toString,           // Operator[string, int, string]
)  // Decode[string, string]

result := decoder("42")  // Success("42")
result := decoder("-5")  // Failures([...])

func Ap

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

Ap creates an operator that applies a function decoder to a value decoder. This is the curried version of MonadAp, useful for composition pipelines.

Example:

apOp := decode.Ap[string](decode.Of[string](42))
decoderFn := decode.Of[string](func(n int) string {
    return fmt.Sprintf("Number: %d", n)
})
result := apOp(decoderFn)

func ApS added in v2.2.6

func ApS[I, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa Decode[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 Decode[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) Decode[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) Decode[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 Decode 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 creates an operator that sequences decode operations. This is the curried version of MonadChain, useful for composition pipelines.

Example:

chainOp := decode.Chain(func(n int) Decode[string, string] {
    return decode.Of[string](fmt.Sprintf("Number: %d", n))
})
decoder := chainOp(decode.Of[string](42))

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 Decode.

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 decoded values. This is the curried version of MonadMap, useful for composition pipelines.

Example:

mapOp := decode.Map(func(n int) string {
    return fmt.Sprintf("Number: %d", n)
})
decoder := mapOp(decode.Of[string](42))

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. This is an alias for reader.Reader[R, A], which is func(R) A.

In the decode context, Reader is used to access the input data being decoded. The environment R is typically the raw input (e.g., JSON, string, bytes) that needs to be decoded into a structured type A.

Example:

// A reader that extracts a field from a map
getField := func(data map[string]any) string {
    return data["name"].(string)
}  // Reader[map[string]any, string]

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. This is an alias for validation.Validation[A], which is Either[Errors, A].

In the decode context:

  • Left(Errors): Decoding failed with one or more validation errors
  • Right(A): Successfully decoded value of type A

Example:

// Success case
valid := validation.Success(42)  // Right(42)

// Failure case
invalid := validation.Failures[int](validation.Errors{
    &validation.ValidationError{Messsage: "invalid format"},
})  // Left([...])

Jump to

Keyboard shortcuts

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