Funx.Validate (funx v0.8.0)

View Source

Declarative validation DSL using optics and applicative error accumulation.

Overview

The Validation DSL provides:

  • Declarative syntax for validating nested structures
  • Projection to fields using optics (Lens, Prism, Traversal)
  • Applicative error accumulation (all validators run, all errors collected)
  • Structure preservation (returns original value on success)

Usage

use Funx.Validate

user_validation =
  validate do
    at :name, [Required, {MinLength, min: 3}]
    at :email, [Required, Email]
  end

Either.validate(%{name: "Alice", email: "alice@example.com"}, user_validation)
#=> %Right{right: %{name: "Alice", email: "alice@example.com"}}

Laws

  1. Identity: validate do end returns Right(value)
  2. Structure Preservation: Successful validation returns original structure
  3. Applicative: All validators run; all errors accumulate

Architecture

The DSL compiles in two phases:

  1. Parser - Converts DSL syntax into Step nodes
  2. Executor - Converts Step nodes into executable validator function

Summary

Functions

Defines a validation using the DSL.

Types

Functions

validate(opts \\ [], list)

(macro)

Defines a validation using the DSL.

Returns a validator function compatible with Either.validate/2,3.

Syntax

Root Validators

validate do
  HasContactMethod
  ValidTimezone
end

Field Validation with at

validate do
  at :name, Required
  at :email, Email
end

By default, at :key uses Prism.key(:key) (optional field).

Nested Field Validation (List Paths)

validate do
  at [:user, :profile, :name], Required
  at [:user, :profile, :age], Positive
end

List paths support struct modules for type-safe nested access:

validate do
  at [User, :profile, Profile, :age], Positive
end

Explicit Optics

validate do
  # Prism: optional field
  at Prism.key(:email), Email

  # Lens: required field (raises KeyError if missing)
  at Lens.key(:name), Required
end

Validator Options

validate do
  at :name, {MinLength, min: 3}
end

Multiple Validators per Field

validate do
  at :name, [Required, {MinLength, min: 3}]
end

Validators run sequentially left-to-right.

Validation Modes

# Sequential mode (default): fail-fast, short-circuits on first error
validate mode: :sequential do
  at :name, Required
  at :email, Email
end

# Parallel mode: runs all validations, accumulates all errors
validate mode: :parallel do
  at :name, Required
  at :email, Email
end

Examples

validate do
  at :name, Required
  at :email, [Required, Email]
  at :age, Positive
end