validate_monadic

Types

Simple type alias for a non-empty list. A non-empty list is just a structure containing the first item of the list, followed by the rest of the list. You can find a more useful implementation here . For the purposes of not including an extra dependency, we just use a tuple here.

pub type ErrorList(error) =
  #(error, List(error))

Simple type alias over a Result with a non-empty list off generic errors for the Error branch. The non-empty list is important here. For validation we must represent a Result type that can have multiple errors, but we must avoid allowing something like Error([]) in which we can have an error branch but no errors!

pub type Validation(validated, error) =
  Result(validated, ErrorList(error))

Functions

pub fn and_also(
  validation_a: Result(a, #(b, List(b))),
  validation_b: Result(a, #(b, List(b))),
) -> Result(a, #(b, List(b)))

Combine two validation results into one. This mainly for merging errors. The Ok branch of the last validation supplied will be the returned Ok branch. This is used internally by compose

pub fn and_map(
  prev prev_validation: Result(fn(a) -> b, #(c, List(c))),
  next validation: Result(a, #(c, List(c))),
) -> Result(b, #(c, List(c)))

Used to create applicative chains of validation. This is very important for combining validation of fields into the validation of an entire form.

let validate_form = function.curry3(fn (
    ValidFirstName,
    ValidLastName,
    ValidAge
  ) {
    ValidForm(ValidFirstName, ValidLastName, ValidAge)
  })

let validation_result =
  validate.succeed(validate_form)
  |> validate.and_map(first_name_result)
  |> validate.and_map(last_name_result)
  |> validate.and_map(age_result)
pub fn and_then(
  over validation: Result(a, #(b, List(b))),
  bind bind_fn: fn(a) -> Result(c, #(b, List(b))),
) -> Result(c, #(b, List(b)))

Specify a validation that will run after a given validation, using its result. This is very useful for validations that need to run after a transform is attempted.

let age_result =
  form.age_string
  |> is_parsable_int
  |> validate.and_then(int_less_than(_, 101))

It can easily be used in conjuction with compose

let age_result =
  form.age_string
  |> is_parsable_int
  |> validate.and_then(
    validate.compose(int_less_than(_, 101), [int_greater_than(_, 0)])
  )
pub fn compose(
  input: a,
  validation: fn(a) -> Result(b, #(c, List(c))),
  validations: List(fn(a) -> Result(b, #(c, List(c)))),
) -> Result(b, #(c, List(c)))

Compose together multiple validations. This combine the errors of all validations that fail, and does not stop at the first failure. Takes the input to be validated as the first argument, then a non-empty list of unary functions that take that input and return a validation result of the same type.


let validation_result =
  raw_field
    |> validate.compose(no_numbers, [shorter_than_10, no_symbols, no_whitespace])
pub fn error(err: a) -> Result(b, #(a, List(a)))

Convenience function for lifting a single error into our non-empty list error.

pub fn map(
  over validation: Result(a, #(b, List(b))),
  with map_fn: fn(a) -> c,
) -> Result(c, #(b, List(b)))

Map a validation type to another type. This is often useful to nest the result of a validation into a “ValidatedType”. This is just an alias over result.map

  pub type ValidatedLastName {
    ValidatedLastName(String)
  }

  // ...

  let last_name_result =
    form.last_name
      |> string_non_empty
      |> validate.map(ValidatedLastName)
pub fn map_error(
  over validation: Result(a, #(b, List(b))),
  with map_fn: fn(b) -> c,
) -> Result(a, #(c, List(c)))

Map over all the errors for a validation result. This is very useful for cases where you have re-usable validators with generic error messages, and you wish to specify the errors are associated with a specific field


let validation_result =
  raw_field
    |> validate.compose(no_numbers, [shorter_than_10])
    |> validate.map_error(string.append("Field Name Error: ", _))
pub fn succeed(a: a) -> Result(a, #(b, List(b)))

Convenience function for lifting a value into our validation type’s Ok branch, like other methods in this module, it is literally just an alias for a Result type method.

Search Document