codec

package
v2.1.23 Latest Latest
Warning

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

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

Documentation

Overview

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func Pipe

func Pipe[A, B, O, I any](ab Type[B, A, A]) func(Type[A, O, I]) Type[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]

Types

type Codec

type Codec[I, O, A any] struct {
	Decode decoder.Decoder[I, A]
	Encode encoder.Encoder[O, A]
}

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.

type Context

type Context = validation.Context

Context provides contextual information for validation operations, such as the current path in a nested structure.

type Decode

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

Decode is a function that decodes input I to type A with validation. It returns a Validation result directly.

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.

type Encode

type Encode[A, O any] = Reader[A, O]

Encode is a function that encodes type A to output O.

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.

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.

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 Lazy

type Lazy[A any] = lazy.Lazy[A]

Lazy represents a lazily evaluated value.

type Option

type Option[A any] = option.Option[A]

Option represents an optional value that may or may not be present.

type Pair

type Pair[L, R any] = pair.Pair[L, R]

Pair represents a tuple of two values of types L and R.

type Prism added in v2.1.19

type Prism[S, A any] = prism.Prism[S, A]

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

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

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

type Refinement[A, B any] = Prism[A, B]

Refinement represents the concept that B is a specialized type of A

type Result

type Result[A any] = result.Result[A]

Result represents a computation that may fail with an error.

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.

func Array added in v2.1.19

func Array[T, O any](item Type[T, O, any]) Type[[]T, []O, any]

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 Bool

func Bool() Type[bool, bool, any]

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

func Int() Type[int, int, any]

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 MakeSimpleType

func MakeSimpleType[A any]() Type[A, A, any]

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 Nil

func Nil[A any]() Type[*A, *A, any]

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 String

func String() Type[string, string, any]

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]

type Validate

type Validate[I, A any] = validate.Validate[I, A]

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.

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.

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.

Jump to

Keyboard shortcuts

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