Description Usage Arguments How to specify validation checks How to specify error messages See Also Examples
The main functions of rong apply or undo input validation checks to functions.
firmly()
transforms a function into a function with input validation
checks
fasten()
takes a set of input validations and returns an operator
that applies the input validations to functions (i.e., it
curries firmly()
)
loosely()
undoes the application of firmly()
, by returning the
original function (without checks)
These are supplemented by:
vld_error_cls()
, which extracts the subclass of the error condition
that is signaled when an input validation error occurs
is_firm()
, which checks whether an object is a firmly applied function,
i.e., is created by firmly()
1 2 3 4 5 6 7 8 9 |
f |
Function. |
... |
Input validation checks (with support for quasiquotation). |
error_class |
Subclass of the error condition to be raised when an input
validation error occurs (character). If |
x |
Object to test. |
A validation check is specified
by a predicate function: if the predicate yields TRUE
, the check passes,
otherwise the check fails. Any predicate function will do, provided its
first argument is the object to be checked.
Simply write the predicate when you want to apply it to all (named) arguments.
Example — To transform the function
1 | add <- function(x, y, z) x + y + z
|
so that every argument is checked to be numeric, use the predicate
is.numeric()
:
1 2 3 | add_num <- firmly(add, is.numeric)
add_num(1, 2, 3) # 6
add_num(1, 2, "three") # Error: 'FALSE: is.numeric(z)'
|
Specifiy expressions (of arguments) when you want to restrict the scope of a check.
Example — To require that y
and z
are numeric (but not x
necessarily), specify them as arguments of is.numeric()
(this is valid
even though is.numeric()
, as a function, only takes a single argument):
1 2 3 | add_num_yz <- firmly(add, is.numeric(y, z))
add_num_yz(TRUE, 2, 3) # 6
add_num_yz(TRUE, TRUE, 3) # Error: 'FALSE: is.numeric(y)'
|
If a predicate has (named) parameters, you can set them as part of the check. The format for setting the parameters of a predicate, as a validation check, is
1 | predicate(<params_wo_default_value>, ..., <params_w_default_value>)
|
where ...
is filled by the expressions to check, which you may omit when
you intend to check all arguments. The order of predicate arguments is
preserved within the two groups (parameters without default value vs those
with default value).
Thus the rule for setting the parameters of predicate()
as a validation
check is the same as that of predicate()
as a function.
Example — You can match a regular expression with the following wrapper
around grepl()
:
1 2 3 |
As a validation check, the format for setting the parameters of this predicate is
1 |
Thus the value of regex
must be set, and may be matched by position.
Setting case_sensitive
is optional, and must be done by name.
1 2 3 4 5 6 |
Short predicates of a single argument can be succinct expressed by their
body alone (enclosed in curly braces). Use .
(dot) to indicate the
argument.
Example — Monotonicity of arguments can be expressed using an ordinary (anonymous) function declaration
1 2 3 |
or more succinctly like so
1 2 3 |
You don't have to specify them at all—they are automatically generated by default, and are typically informative enough to enable you to identify the cause of failure. Nonetheless, you can make errors more comprehensible, and poinpoint their source more quickly, by providing additional contextual information.
Generally, error messages for validation checks are set by attaching them to the predicate. The syntax is
1 | <error_message> := predicate
|
In the simplest case, <error_message>
is just a literal string, such as
"x is not positive"
. But it can also be a “smart string,” which
can encode context-specific information.
TODO
TODO
TODO:
- scope/context of string interpolation (meaning of {{.}}
)
- Two interpretations of dot (is this best?)
- use of pronouns .expr
, .value
vld_spec()
, vld_exprs()
, validate, predicates,
new_vld_error_msg()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | bc <- function(x, y) c(x, y, 1 - x - y)
## Ensure that inputs are numeric
bc1 <- firmly(bc, is.numeric)
bc1(.5, .2)
## Not run:
bc1(.5, ".2")
## End(Not run)
## Use custom error messages
bc2 <- firmly(bc, "{{.}} is not numeric (type: {typeof(.)})" := is.numeric)
## Not run:
bc2(.5i, ".2")
## End(Not run)
## Fix values using Tidyverse quasiquotation
z <- 0
in_triangle <- vld_spec(
"{{.}} is not positive (value is {.})" :=
{isTRUE(. > !! z)}(x, y, 1 - x - y)
)
bc3 <- firmly(bc, is.numeric, !!! in_triangle)
bc3(.5, .2)
## Not run:
bc3(.5, .6)
## End(Not run)
## Highlight the core logic with fasten()
bc_clean <- fasten(
"{{.}} is not a number" := {is.numeric(.) && length(.) == 1},
"{{.}} is not positive" :=
{isTRUE(. > 0)}(x, "y is not in the upper-half plane" := y, 1 - x - y)
)(
function(x, y) {
c(x, y, 1 - x - y)
}
)
## Recover the underlying function with loosely()
loosely(bc_clean)
|
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.