checks
Argument type checking API.
This library declares a checks()
function and a checkers
table, which
allow to check the parameters passed to a Lua function in a fast and
unobtrusive way.
checks (type_1, ..., type_n)
, when called directly inside function
f
, checks that f
's 1st argument conforms to type_1
, that its 2nd
argument conforms to type_2
, etc. until type_n
. Type specifiers
are strings, and if the arguments passed to f
don't conform to their
specification, a proper error message is produced, pinpointing the
call to f
as the faulty expression.
Each type description type_n
must be a string, and can describe:
"table"
, "number"
etc.;__type
field of
the argument's metatable;checkers
global table. This table uses type names as keys, test functions
returning Booleans as keys.Moreover, types can be prefixed with a "?"
, which makes them
optional. For instance, "?table"
accepts tables as well as nil
values.
A "?"
alone accepts anything. It is mainly useful as a placeholder,
to skip an argument which doesn't need to be checked.
Finally, several types can be accepted, if their names are
concatenated with a bar "|"
between them. For instance,
"table|number"
accepts tables as well as numbers. It can be combined
with the question mark, so "?table|number"
accepts tables, numbers
and nil values. It is actually equivalent to "nil|table|number"
.
More formally, let's specify conform(a, t)
, the property that
argument a
conforms to the type denoted by t
. conform(a,t)
is
true if and only if at least one of the following propositions is
verified:
conforms(a, t:match "^(.-)|.*"
t == "?"
t:sub(1, 1) == "?" and (conforms(a, t:sub(2, -1)) or a==nil)
type(a) == t
getmetatable(a) and getmetatable(a).__type == t
checkers[t] and checkers[t](a) is true
conforms(a, t:match "^.-|(.*)")
The above propositions are listed in the order in which they are
tried by check
. The higher they appear in the list, the faster
checks
accepts aconforming argument. For instance,
checks("number")
is faster than `checkers.mynumber(x) return
type(x)=="number" end; checks("mynumber")`.
require 'checks'
-- Custom checker function --
function checkers.port(p)
return type(p)=='number' and p>0 and p<0x10000
end
-- A new named type --
socket_mt = { __type='socket' }
asocket = setmetatable ({ }, socket_mt)
-- A function that checks its parameters --
function take_socket_then_port_then_maybe_string (sock, port, str)
checks ('socket', 'port', '?string')
end
take_socket_then_port_then_maybe_string (asocket, 1024, "hello")
take_socket_then_port_then_maybe_string (asocket, 1024)
-- A couple of other parameter-checking options --
function take_number_or_string()
checks("number|string")
end
function take_number_or_string_or_nil()
checks("?number|string")
end
function take_anything_followed_by_a_number()
checks("?", "number")
end
-- Catch some incorrect arguments passed to the function --
function must_fail(...)
assert (not pcall (take_socket_then_port_then_maybe_string, ...))
end
must_fail ({ }, 1024, "string") -- 1st argument isn't a socket
must_fail (asocket, -1, "string") -- port number must be 0-0xffff
must_fail (asocket, 1024, { }) -- 3rd argument cannot be a table
checks(level, varargs) | check whether the calling function's argument have the expected types. |
error(level, narg, expected, got) | Generate and throw an error. |
matches(actualType, expectedTypes) | Return true iff actualType occurs in expecteTypes, the later being a list of type names separate by '|' chars. |
checkers
checks
checks(level, varargs)
checks( [level], t_1, ..., t_n)
causes an error if the type of
argument #i
in stack frame #level
isn't as described by t_i
, for
i in [1...n]
. level
is optional, it defaults to one (checks the
function immediately calling checks
).
level
:
the number of stack levels to ignore in the error message,
should it be produced. Optional, defaults to 1.
varargs
:
one type string per expected argument.
nothing on success, throw an error on failure.
error(level, narg, expected, got)
level
:
stack level where the error must be reported
narg
:
indice of the erroneous argument
expected
:
name of the expected type
got
:
name of the type actually found
never returns (throws a Lua error instead)
matches(actualType, expectedTypes)
WITH_SUM_TYPES
is disabled, the expectedTypes list must have one
element, i.e. no '|' separator character.
actualType
:
the type of the tested object
expectedTypes
:
the list of types listed as acceptable in checks()
for this argument
whether actualType
is listed in expectedTypes
.
checkers
Table of custom type-checkers.
This table contain type-checking
functions, indexed by type name. If an argument a
is expected to be
of type t
, and neither type(a)
nor getmetatable(a).__type
return
t
, but checkers[t]
contains a function, this function will be
called, with a
as its only argument. If the function returns true
,
then a
is considered to be of type t
.
-- Create the type-checking function --
function checkers.positive_number(x)
return type(x)=='number' and x>0
end
-- Use the `positive_number` type-checking function --
function sqrt(x)
checks('positive_number')
return x^(1/2)
end
checks