Module typecheck
Gradual type checking for Lua functions.
The behaviour of the functions in this module are controlled by the value
of the argcheck
field maintained by the std._debug
module. Not setting
a value prior to loading this module is equivalent to having argcheck = true
.
The first line of Lua code in production quality applications that value execution speed over rigorous function type checking should be:
require 'std._debug' (false)
Alternatively, if your project also depends on other std._debug
hints
remaining enabled:
require 'std._debug'.argcheck = false
This mitigates almost all of the overhead of type checking with the functions from this module.
Functions
check (expected, argu, i, predicate) | Low-level type conformance check helper. |
argcheck (name, i, expected, actual[, level=2]) | Check the type of an argument against expected types. |
argerror (name, i[, extramsg[, level=1]]) | Raise a bad argument error. |
argscheck (decl, inner) | Wrap a function definition with argument type and arity checking. |
check (expected, actual) | Checks the type of actual against the expected typespec |
extramsg_mismatch ([i], expected, argu[, key]) | Format a type mismatch error. |
extramsg_toomany (bad, expected, actual) | Format a too many things error. |
opt (...) | Create an ArgCheck predicate for an optional argument. |
parsetypes (types) | Compact permutation list into a list of valid types at each argument. |
resulterror (name, i[, extramsg[, level=1]]) | Raise a bad result error. |
typesplit (either) | Split a typespec string into a table of normalized type names. |
Tables
types | A collection of ArgCheck functions used by normalize APIs. |
Fields
ARGCHECK_FRAME | Add this to any stack frame offsets when argchecks are in force. |
Metamethods
__index (name) | Lazy loading of typecheck modules. |
Types
ArgCheck (argu, index) | Signature of an argscheck callable. |
Predicate (x) | Signature of a check type predicate callable. |
Functions
- check (expected, argu, i, predicate)
-
Low-level type conformance check helper.
Use this, with a simple Predicate function, to write concise argument type check functions.
Parameters:
- expected string name of the expected type
- argu
table
a packed table (including
n
field) of all arguments - i int index into argu for argument to action
- predicate
Predicate
check whether
argu[i]
matchesexpected
Usage:
function callable(argu, i) return check('string', argu, i, function(x) return type(x) == 'string' end) end
- argcheck (name, i, expected, actual[, level=2])
-
Check the type of an argument against expected types.
Equivalent to luaL_argcheck in the Lua C API.
Call
argerror
if there is a type mismatch.Argument
actual
must match one of the types from inexpected
, each of which can be the name of a primitive Lua type, a stdlib object type, or one of the special options below:#table accept any non-empty table any accept any non-nil argument type callable accept a function or a functable file accept an open file object func accept a function function accept a function functable accept an object with a __call metamethod int accept an integer valued number list accept a table where all keys are a contiguous 1-based integer range #list accept any non-empty list object accept any std.Object derived type :foo accept only the exact string ':foo', works for any :-prefixed string
The
:foo
format allows for type-checking of self-documenting boolean-like constant string parameters predicated onnil
versus:option
instead offalse
versustrue
. Or you could support both:argcheck('table.copy', 2, 'boolean|:nometa|nil', nometa)
A very common pattern is to have a list of possible types including 'nil' when the argument is optional. Rather than writing long-hand as above, prepend a question mark to the list of types and omit the explicit 'nil' entry:
argcheck('table.copy', 2, '?boolean|:nometa', predicate)
Normally, you should not need to use the
level
parameter, as the default is to blame the caller of the function usingargcheck
in error messages; which is almost certainly what you want.Parameters:
- name string function to blame in error message
- i int argument number to blame in error message
- expected string specification for acceptable argument types
- actual argument passed
- level int call stack level to blame for the error (default 2)
Usage:
local function case(with, branches) argcheck('std.functional.case', 2, '#table', branches) ...
- argerror (name, i[, extramsg[, level=1]])
-
Raise a bad argument error.
Equivalent to luaL_argerror in the Lua C API. This function does not
return. The
level
argument behaves just like the coreerror
function.Parameters:
- name string function to callout in error message
- i int argument number
- extramsg string additional text to append to message inside parentheses (optional)
- level int call stack level to blame for the error (default 1)
See also:
Usage:
local function slurp(file) local h, err = input_handle(file) if h == nil then argerror('std.io.slurp', 1, err, 2) end ...
- argscheck (decl, inner)
-
Wrap a function definition with argument type and arity checking. In addition to checking that each argument type matches the corresponding element in the types table with
argcheck
, if the final element of types ends with an ellipsis, remaining unchecked arguments are checked against that type:format = argscheck('string.format(string, ?any...)', string.format)
A colon in the function name indicates that the argument type list does not have a type for
self
:format = argscheck('string:format(?any...)', string.format)
If an argument can be omitted entirely, then put its type specification in square brackets:
insert = argscheck('table.insert(table, [int], ?any)', table.insert)
Similarly return types can be checked with the same list syntax as arguments:
len = argscheck('string.len(string) => int', string.len)
Additionally, variant return type lists can be listed like this:
open = argscheck('io.open(string, ?string) => file or nil, string', io.open)
Parameters:
- decl string function type declaration string
- inner func function to wrap with argument checking
Usage:
local case = argscheck('std.functional.case(?any, #table) => [any...]', function(with, branches) ... end) -- Alternatively, as an annotation: local case = argscheck 'std.functional.case(?any, #table) => [any...]' .. function(with, branches) ... end
- check (expected, actual)
-
Checks the type of actual against the expected typespec
Parameters:
- expected string expected typespec
- actual object being typechecked
Returns:
-
bool
true
, if actual matches expectedOr
-
nil
- string an extramsg_mismatch format error message, otherwise
Usage:
--> stdin:2: string or number expected, got empty table assert(check('string|number', {}))
- extramsg_mismatch ([i], expected, argu[, key])
-
Format a type mismatch error.
Parameters:
- i int index of argu to be matched with (optional)
- expected string a pipe delimited list of matchable types
- argu table packed table of all arguments
- key erroring container element key (optional)
Returns:
See also:
Usage:
if fmt ~= nil and type(fmt) ~= 'string' then argerror('format', 1, extramsg_mismatch(1, '?string', argu)) end
- extramsg_toomany (bad, expected, actual)
-
Format a too many things error.
Parameters:
- bad string the thing there are too many of
- expected int maximum number of bad things expected
- actual int actual number of bad things that triggered the error
See also:
Usage:
if select('#', ...) > 7 then argerror('sevenses', 8, extramsg_toomany('argument', 7, select('#', ...))) end
- opt (...)
-
Create an ArgCheck predicate for an optional argument.
This function satisfies the ArgCheck interface in order to be useful as an argument to argscheck when a particular argument is optional.
Parameters:
- ... ArgCheck type predicate callables
Returns:
-
ArgCheck
a new function that calls all passed
predicates, and combines error messages if all fail
Usage:
getfenv = argscheck( 'getfenv', opt(types.integer, types.callable) ) .. getfenv
- parsetypes (types)
-
Compact permutation list into a list of valid types at each argument.
Eliminate bracketed types by combining all valid types at each position
for all permutations of typelist.
Parameters:
- types list a normalized list of type names
Returns:
-
list
valid types for each positional parameter
- resulterror (name, i[, extramsg[, level=1]])
-
Raise a bad result error.
Like argerror for bad results. This function does not
return. The
level
argument behaves just like the coreerror
function.Parameters:
- name string function to callout in error message
- i int result number
- extramsg string additional text to append to message inside parentheses (optional)
- level int call stack level to blame for the error (default 1)
Usage:
local function slurp(file) ... if type(result) ~= 'string' then resulterror('std.io.slurp', 1, err, 2) end
- typesplit (either)
-
Split a typespec string into a table of normalized type names.
Parameters:
Returns:
-
table
a new list with duplicates removed and leading '?'s
replaced by a 'nil' element
Tables
- types
-
A collection of ArgCheck functions used by
normalize
APIs.Fields:
Fields
Metamethods
- __index (name)
-
Lazy loading of typecheck modules.
Don't load everything on initial startup, wait until first attempt
to access a submodule, and then load it on demand.
Parameters:
- name string submodule name
Returns:
-
table or nil
the submodule that was loaded to satisfy the missing
`name`, otherwise `nil` if nothing was found
Usage:
local version = require 'typecheck'.version
Types
- ArgCheck (argu, index)
-
Signature of an argscheck callable.
Parameters:
- argu
table
a packed table (including
n
field) of all arguments - index int into @argu* for argument to action
Returns:
-
nothing, to accept
argu[i]
Or
-
string
error message, to reject
argu[i]
immediatelyOr
Usage:
len = argscheck('len', any(types.table, types.string)) .. len
- argu
table
a packed table (including
- Predicate (x)
-
Signature of a check type predicate callable.
Parameters:
- x object to action
Returns:
-
boolean
true
if x is of the expected type, otherwisefalse
- string description of the actual type for error message