validated

This module provides the Validated type and associated functions to accumulate errors in an ergonomic way.

Example

fn validate_form(email: String, age: Int) -> Validated(Form, String) {
  use email <- v.do(validate_email(email))
  use age <- v.do(validate_age(age))
  Valid(Form(email:, age:))
}

validated_form("lucy@example.com", 20)
// -> Valid(Form("lucy@example.com", 20))

validated_form("asdf", 5)
// -> Invalid(Form("", 0), ["not a valid email", "must be 18 or older"])

This API is possible because a Validated requires a “default” value in case of failure. That way, the default value is passed through to continue the validation.

Since a value within a validation block may or may not be the default value, it is important to never perform side-effects inside the validation.

Types

Validated represents the result of something that may succeed or not. It is similar to Result, except that it will accumulate multiple errors instead of stopping at the first error. Valid means it was successful, Invalid means it was not successful.

pub type Validated(a, e) {
  Valid(a)
  Invalid(a, List(e))
}

Constructors

  • Valid(a)
  • Invalid(a, List(e))

    Invalid requires a default value to enable validation in use expressions

A function that takes some input and returns a Validated

pub type Validator(in, out, error) =
  fn(in) -> Validated(out, error)

Functions

pub fn all(
  validateds: List(Validated(a, b)),
) -> Validated(List(a), b)

Combines a list of Validateds into a single Validated. If all elements in the list are Valid then the function returns a Valid holding the list of values. Otherwise an Invalid is returned that combines all the errors. Valid([]) is returned if the list is empty.

pub fn bit_array(
  value: Result(BitArray, a),
) -> Validated(BitArray, a)

Creates a Validated from a Result. It uses an empty BitArray as the default value in case of failure.

pub fn bool(value: Result(Bool, a)) -> Validated(Bool, a)

Creates a Validated from a Result. It uses False as the default value in case of failure.

pub fn dict(
  value: Result(Dict(a, b), c),
) -> Validated(Dict(a, b), c)

Creates a Validated from a Result. It uses an empty Dict as the default value in case of failure.

pub fn do(
  validated: Validated(a, b),
  next: fn(a) -> Validated(c, b),
) -> Validated(c, b)

“Updates” a Valid value by passing its value to a function that yields a Validated, and returning the yielded Validated. (This may “replace” the Valid with an Invalid.)

If the input is an Invalid rather than an Valid, the function is still called using the default value from the Invalid. If the function succeeds, the Invalid’s default is replaced with the Valid value. If the function fails, the first Invalid errors are combined with the returned Invalid errors.

This function is useful in use expressions to ergonomically validate fields of a record

Examples

use a <- do(Valid(1))
use b <- do(Valid(2))
Valid(#(a, b))
// -> Valid(#(1, 2))
use a <- do(Invalid(0, [Nil]))
use b <- do(Valid(2))
use c <- do(Invalid(0, [Nil]))
Valid(#(a, b, c))
// -> Invalid(#(0, 2, 0), [Nil, Nil])
pub fn flatten(
  validated: Validated(Validated(a, b), b),
) -> Validated(a, b)

Merges a nested Validated into a single layer.

pub fn float(value: Result(Float, a)) -> Validated(Float, a)

Creates a Validated from a Result. It uses 0.0 as the default value in case of failure.

pub fn guard(
  validated: Validated(a, b),
  default: c,
  continue: fn(a) -> Validated(c, b),
) -> Validated(c, b)

Like do, but does not continue accumulating further errors if the Validated is Invalid.

pub fn int(value: Result(Int, a)) -> Validated(Int, a)

Creates a Validated from a Result. It uses 0 as the default value in case of failure.

pub fn is_invalid(validated: Validated(a, b)) -> Bool

Checks whether the Validated is Invalid

pub fn is_valid(validated: Validated(a, b)) -> Bool

Checks whether the Validated is Valid

pub fn lazy_guard(
  validated: Validated(a, b),
  default: fn() -> c,
  continue: fn(a) -> Validated(c, b),
) -> Validated(c, b)

Like guard but accepts a callback for the default value in case the Validated is Invalid.

pub fn list(value: Result(List(a), b)) -> Validated(List(a), b)

Creates a Validated from a Result. It uses an empty list as the default value in case of failure.

pub fn map(
  validated: Validated(a, b),
  f: fn(a) -> c,
) -> Validated(c, b)

Updates a value held within the Valid of a Validated by calling a given function on it. If the Validated is an Invalid rather than Valid, the function is called on the default value.

pub fn map_error(
  validated: Validated(a, b),
  f: fn(List(b)) -> List(c),
) -> Validated(a, c)

Updates a value held within the Invalid of a Validated by calling a given function on it. If the result is Valid rather than Invalid the function is not called and the result stays the same.

pub fn optional(
  value: Result(Option(a), b),
) -> Validated(Option(a), b)

Creates a Validated from a Result. It uses None as the default value in case of failure.

pub fn replace(
  validated: Validated(a, b),
  value: c,
) -> Validated(c, b)

Replace the value within a Validated

pub fn result(value: Result(a, b), default: a) -> Validated(a, b)

Creates a Validated from a Result. It requires a default value in case of failure.

pub fn run(
  validator: fn(a) -> Validated(b, c),
  input: a,
) -> Validated(b, c)

Run a Validator function.

pub fn run_all(
  validators: List(fn(a) -> Validated(b, c)),
  input: a,
) -> Validated(Nil, c)

Run all the Validators in order on the given input. It will accumulate all the errors from all of the Validators.

If there are no errors, or if the list is empty, Valid(Nil) is returned.

pub fn string(value: Result(String, a)) -> Validated(String, a)

Creates a Validated from a Result. It uses an empty string as the default value in case of failure.

pub fn to_result(
  validated: Validated(a, b),
) -> Result(a, List(b))

Convert a Validated into a Result

pub fn unwrap(validated: Validated(a, b)) -> a

Return the Valid value of the validated, or the default value if it is Invalid.

Search Document