Closed egnha closed 7 years ago
The API for input validation checks has to change: we should use quosures instead formulas, so that we can use tidyeval for the construction of check expressions while (crucially) still using standard evaluation to perform input validation (for performance reasons).
The new API will reserve the use of formulas as a mechanism to pair predicates with their scope of application, but not in order to capture an eval-environment, which is the job of the quosure.
Sketch of new input validation API:
library(rlang)
msg1 <- "Not positive"
msg2 <- "Not greater than 1"
predicate <- is.vector
a <- 1
zero <- 0
# re-export quos for valaddin
chks <- quos(
# predicates are "bare"
is.numeric,
!!predicate,
# retain lambda-expressions
!!msg1 := {. > UQ(zero)},
# string interpolation via glue(); scope now goes on the RHS of formula
"{.} is not an integer" = is.integer ~ x,
# quasiquotation possible with quosures
{. > 0} ~ quos(!!msg2 := x - !!a, y)
)
gives
chks
#> [[1]]
#> <quosure: global>
#> ~is.numeric
#>
#> [[2]]
#> <quosure: empty>
#> ~function (x, mode = "any")
#> .Internal(is.vector(x, mode))
#>
#> $`Not positive`
#> <quosure: global>
#> ~{
#> . > 0
#> }
#>
#> $`{.} is not an integer`
#> <quosure: global>
#> ~(is.integer ~ x)
#>
#> [[5]]
#> <quosure: global>
#> ~({
#> . > 0
#> } ~ quos(`:=`("Not greater than 1", x - 1), y))
#>
#> attr(,"class")
#> [1] "quosures"
chks
can then be parsed and feed into the existing input-validation procedure via validating_closure()
(with some minor tweaks).
Formulas are superfluous, as a pairing mechanism, for we can just use quos()
within quos()
, together with the convention that the first argument is the predicate function.
Example:
a <- 1
msg <- "Not positive"
local_msg <- "Not greater than {a}"
quos(
# global check (which is to get an auto-gen message)
is.numeric,
# local check (with custom message, overridden by local messages)
!!msg := quos({. > 0}, x, !!local_msg := y - !!a)
)
#> [[1]]
#> <quosure: global>
#> ~is.numeric
#>
#> $`Not positive`
#> <quosure: global>
#> ~quos({
#> . > 0
#> }, x, `:=`("Not greater than {a}", y - 1))
#>
#> attr(,"class")
#> [1] "quosures"
This is already working with
lazyeval::f_list()
andlazyeval::f_new()
, but should be replaced by equivalent functions in rlang (quos()
,new_quosure()
, resp.).