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
nfield) 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
argerrorif there is a type mismatch.Argument
actualmust 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 stringThe
:fooformat allows for type-checking of self-documenting boolean-like constant string parameters predicated onnilversus:optioninstead offalseversustrue. 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
levelparameter, as the default is to blame the caller of the function usingargcheckin 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
levelargument behaves just like the coreerrorfunction.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 failUsage:
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
levelargument behaves just like the coreerrorfunction.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
normalizeAPIs.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 foundUsage:
local version = require 'typecheck'.version
Types
- ArgCheck (argu, index)
-
Signature of an argscheck callable.
Parameters:
- argu
table
a packed table (including
nfield) 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
trueif x is of the expected type, otherwisefalse - string description of the actual type for error message