Argument validation helpers for ExCellerate.Function implementations.
These functions validate that arguments meet expected type or structural
constraints at runtime, raising ExCellerate.Error with a descriptive
message on failure. They are designed to reduce boilerplate in custom
function modules and produce consistent error messages.
Every function accepts a func_name parameter (the name of the calling
function) so error messages clearly identify which function received
bad input.
Usage in a Custom Function
defmodule MyApp.Functions.Double do
@behaviour ExCellerate.Function
import ExCellerate.Functions.Guards
def name, do: "double"
def arity, do: 1
def call([n]) do
ensure_number!(n, name())
n * 2
end
endUsage in Built-in Functions
Built-in functions use these helpers in both guard-matched clauses and catch-all error clauses:
def call([str]) when is_binary(str), do: String.upcase(str)
def call([other]) do
ensure_string!(other, name())
end
Summary
Functions
Validates that value is a boolean. Returns value on success.
Validates that value is a date or datetime struct (Date, NaiveDateTime,
or DateTime). Returns value on success.
Validates that value is a recognized date unit string and normalizes
it to the plural form. Returns the plural unit string on success.
Validates that an argument list has an even number of elements. Returns
:ok on success.
Validates that value is an integer. Returns value on success.
Validates that value is a list. Returns value on success.
Validates that value is a non-negative number (>= 0). Returns value
on success.
Validates that value is a number. Returns value on success.
Validates that two lists have the same length. Returns :ok on success.
Validates that value is a string (binary). Returns value on success.
Validates that all lists in a collection have the same length. Returns :ok
on success.
Functions
Validates that value is a boolean. Returns value on success.
Raises ExCellerate.Error with type :runtime if value is not a boolean.
Examples
iex> ExCellerate.Functions.Guards.ensure_boolean!(true, "filter")
true
iex> ExCellerate.Functions.Guards.ensure_boolean!(false, "filter")
false
iex> ExCellerate.Functions.Guards.ensure_boolean!(1, "filter")
** (ExCellerate.Error) Runtime error: 'filter' expects a boolean, got: 1
@spec ensure_date_or_datetime!(any(), String.t()) :: Date.t() | NaiveDateTime.t() | DateTime.t()
Validates that value is a date or datetime struct (Date, NaiveDateTime,
or DateTime). Returns value on success.
Raises ExCellerate.Error with type :runtime if value is not one of
the supported date/time struct types.
Examples
iex> ExCellerate.Functions.Guards.ensure_date_or_datetime!(~D[2024-01-15], "year")
~D[2024-01-15]
iex> ExCellerate.Functions.Guards.ensure_date_or_datetime!(~N[2024-01-15 13:30:00], "year")
~N[2024-01-15 13:30:00]
iex> ExCellerate.Functions.Guards.ensure_date_or_datetime!("2024-01-15", "year")
** (ExCellerate.Error) Runtime error: 'year' expects a Date, NaiveDateTime, or DateTime, got: "2024-01-15"
Validates that value is a recognized date unit string and normalizes
it to the plural form. Returns the plural unit string on success.
Both singular and plural forms are accepted: "day" and "days" both
return "days".
Valid units: "year(s)", "month(s)", "day(s)", "hour(s)",
"minute(s)", "second(s)", "millisecond(s)".
Raises ExCellerate.Error with type :runtime if value is not a valid
date unit.
Examples
iex> ExCellerate.Functions.Guards.ensure_date_unit!("days", "datedif")
"days"
iex> ExCellerate.Functions.Guards.ensure_date_unit!("day", "datedif")
"days"
iex> ExCellerate.Functions.Guards.ensure_date_unit!("weeks", "datedif")
** (ExCellerate.Error) Runtime error: 'datedif' expects a valid unit (year/years, month/months, day/days, hour/hours, minute/minutes, second/seconds, millisecond/milliseconds), got: "weeks"
Validates that an argument list has an even number of elements. Returns
:ok on success.
Use this when a function expects paired arguments (e.g., condition/value
pairs in ifs, or key/list pairs in table).
Raises ExCellerate.Error with type :runtime if the count is zero or odd.
Examples
iex> ExCellerate.Functions.Guards.ensure_even_args!([1, 2, 3, 4], "ifs")
:ok
iex> ExCellerate.Functions.Guards.ensure_even_args!([1, 2, 3], "ifs")
** (ExCellerate.Error) Runtime error: 'ifs' requires an even number of arguments (condition/value pairs)
iex> ExCellerate.Functions.Guards.ensure_even_args!([], "table")
** (ExCellerate.Error) Runtime error: 'table' requires an even number of arguments (condition/value pairs)
Validates that value is an integer. Returns value on success.
Raises ExCellerate.Error with type :runtime if value is not an integer.
Examples
iex> ExCellerate.Functions.Guards.ensure_integer!(5, "left")
5
iex> ExCellerate.Functions.Guards.ensure_integer!(3.5, "left")
** (ExCellerate.Error) Runtime error: 'left' expects an integer, got: 3.5
Validates that value is a list. Returns value on success.
Raises ExCellerate.Error with type :runtime if value is not a list.
Examples
iex> ExCellerate.Functions.Guards.ensure_list!([1, 2, 3], "sum")
[1, 2, 3]
iex> ExCellerate.Functions.Guards.ensure_list!(42, "sum")
** (ExCellerate.Error) Runtime error: 'sum' expects a list, got: 42
Validates that value is a non-negative number (>= 0). Returns value
on success.
Raises ExCellerate.Error with type :runtime if value is not a number
or is negative. The error message always says "expects a non-negative number"
regardless of whether the value was the wrong type or simply negative.
Examples
iex> ExCellerate.Functions.Guards.ensure_non_negative_number!(0, "sqrt")
0
iex> ExCellerate.Functions.Guards.ensure_non_negative_number!(4.5, "sqrt")
4.5
iex> ExCellerate.Functions.Guards.ensure_non_negative_number!(-1, "sqrt")
** (ExCellerate.Error) Runtime error: 'sqrt' expects a non-negative number, got: -1
iex> ExCellerate.Functions.Guards.ensure_non_negative_number!("five", "sqrt")
** (ExCellerate.Error) Runtime error: 'sqrt' expects a non-negative number, got: "five"
Validates that value is a number. Returns value on success.
Raises ExCellerate.Error with type :runtime if value is not a number.
Examples
iex> ExCellerate.Functions.Guards.ensure_number!(42, "sqrt")
42
iex> ExCellerate.Functions.Guards.ensure_number!(3.14, "sqrt")
3.14
iex> ExCellerate.Functions.Guards.ensure_number!("five", "sqrt")
** (ExCellerate.Error) Runtime error: 'sqrt' expects a number, got: "five"
Validates that two lists have the same length. Returns :ok on success.
Use this when a function takes two parallel lists that must correspond
element-by-element (e.g., a data list and a predicate list in filter).
Raises ExCellerate.Error with type :runtime if the lists differ in length.
Examples
iex> ExCellerate.Functions.Guards.ensure_paired_length!([1, 2], [true, false], "filter")
:ok
iex> ExCellerate.Functions.Guards.ensure_paired_length!([1, 2, 3], [true], "filter")
** (ExCellerate.Error) Runtime error: 'filter' expects both lists to have the same length
Validates that value is a string (binary). Returns value on success.
Raises ExCellerate.Error with type :runtime if value is not a string.
Examples
iex> ExCellerate.Functions.Guards.ensure_string!("hello", "upper")
"hello"
iex> ExCellerate.Functions.Guards.ensure_string!(42, "upper")
** (ExCellerate.Error) Runtime error: 'upper' expects a string, got: 42
Validates that all lists in a collection have the same length. Returns :ok
on success.
Use this when a function takes multiple column lists that must all have
the same number of elements (e.g., the value lists in table).
Accepts an empty list (vacuously true). Raises ExCellerate.Error with
type :runtime if any list differs in length.
Examples
iex> ExCellerate.Functions.Guards.ensure_uniform_length!([[1, 2], [3, 4], [5, 6]], "table")
:ok
iex> ExCellerate.Functions.Guards.ensure_uniform_length!([], "table")
:ok
iex> ExCellerate.Functions.Guards.ensure_uniform_length!([[1, 2], [3]], "table")
** (ExCellerate.Error) Runtime error: 'table' expects all lists to have the same length