Description Usage Arguments Value Pattern rules call_match() versus node_match() Named patterns Evaluated bindings Examples
node_match()
provides a flexible way of checking whether a quoted
call conforms to a pattern. It compares the call to a set of
pattern ~ expression
pairs. If the LHS of a formula (e.g. a
pattern) matches, the RHS of the formula is evaluated; otherwise
the next formula is checked. Patterns support named and positional
arguments, and can involve wildcards that match anything.
If the wildcards are named, the part of the call that matched the wildcard will be assigned under that name in the environment where the RHS is evaluated. This technique makes it easy to provide specific responses to different inputs.
1 2 3 | call_match(.x, ..., .env = caller_env())
node_match(.x, ..., .env = caller_env())
|
.x |
An expression to match. |
... |
Two sided formulas with patterns on the LHS and
expressions on the RHS. If the LHS matches |
.env |
An environment in which to evaluate the RHS. By default the current environment. |
The result of evaluating the RHS of a matching pattern.
NULL
if no pattern has matched .x
A pattern typically involves one call (e.g. fn(1, 2)
), and
possibly subcalls (as arguments of an outer call, e.g. fn(other(), 2)
). The arguments of a call in the pattern are checked using the
following rules:
Named arguments will match even if not in order. The patterns
call(foo = 1, bar = 2)
and call(bar = 2, foo = 1)
are thus
equivalent.
Unnamed arguments will match only in order. The patterns
call(foo, bar)
and call(bar, foo)
are thus not equivalent.
NULL-named arguments signal an unnamed argument that should
match even if not in order. The patterns call(NULL = foo, NULL = bar)
and call(NULL = bar, NULL = foo)
will both match the call
call(foo, bar)
.
In addition, patterns can contain wildcards that will match anything:
The .
wildcard will match anything. It can appear on the LHS or
the RHS of an argument. For instance call(. = .)
will match any
argument no matter its name. call(.)
will match any unnamed
argument.
The ...
wildcard matches all remaining unmatched arguments.
call(...)
will match any calls to call()
, including
call(foo)
or call(foo, bar())
. This is in contrast to the
default where matching must be exhaustive, i.e. call(.)
will
match call(foo)
but won't call(foo, bar)
.
If you need to match an argument regardless of whether it has a
name, use the ellipsis as LHS: call(... = .)
.
Binding wildcards with .(name)
. Binding wildcards match
anything and create a reference to the matched code that you can
later refer to for further checking. For instance if you are
matching against the call call(foo(bar))
with the pattern
call(.(arg))
, the arg
object will contain foo(bar)
. If
several wildcards are given the same name, they override each
other and the last one prevails.
Eval-binding wildcards with ..(name)
. These wildcards work just
like binding wildcards but evaluate the matched code before
binding it to a symbol.
Patterns are supplied as formulas with the pattern on the left-hand
side and an expression of your choosing on the right-hand side. The
expression is evaluated only if the pattern match. It can evaluate
to a sentinel value (so you know which expression matched) or to
some checking code. If no pattern matches, the return value of is
NULL
. For example the following returns NULL
:
1 2 3 4 5 |
While this returns 2
:
1 2 3 4 | node_match(expr,
call(foo, baz) ~ 1,
call(foo, .) ~ 2
)
|
call_match()
versus node_match()
call_match()
is just like node_match()
except it standardises
the .x
call and pattern calls with rlang::call_standardise()
.
Standardisation ensures that arguments are matched by position
rather than by name. Note that only function arguments positioned
before ...
are normalised.
Argument names are parsed to R code. This makes it easy to supply binding wildcards as names, e.g.
1 |
On the other hand it makes it more difficult to supply non-syntactic names as you have to double quote. Either one of the following types of double-quotes will work:
1 2 |
It is often useful to check the value of an argument rather than
its symbolic form. The eval-bind wildcard ..()
makes it easy
because it evaluates the matched argument and binds it to a
symbol. You can then refer to that symbol in the right-hand side:
1 | call(. = ..(foo)) ~ is.numeric(foo)
|
However you need to be a bit careful when evaluating R code this way.
First, the code should be evaluated in the right environment. If
you supplied a quosure, then it is evaluated in the quosure
environment. Otherwise, it is evaluated in .env
(the caller
environment by default).
Secondly, the evaluated code might produce side effects, such as
a warning or an error. You might want to wrap your pattern
matching code with e.g. purrr::safely()
.
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | # Let's create a call to dplyr::mutate(). We'll try to match this code
# with various patterns:
call <- quote(mutate(df, weight_sq = weight^2))
# A pattern is an expression supplied as a formula. The pattern is on
# the LHS and the expression on the RHS is evaluated only if the LHS
# matches. Here the second call matches and the RHS evaluates to `2`:
node_match(call,
mutate(df) ~ 1,
mutate(df, weight_sq = weight^2) ~ 2
)
# If further arguments do not matter, use `...` in the pattern:
node_match(call,
mutate(df) ~ 1,
mutate(df, ...) ~ 2
)
# The following patterns do not match because the data frame goes by
# another name. Instead of returning the RHS of a matching pattern,
# node_match() returns `NULL`:
node_match(call,
mutate(x, ...) ~ 1,
mutate(my_data_frame, ...) ~ 2
)
# Let's use a wildcard so the data frame doesn't count. Since we have
# a match, node_match() returns the pattern RHS, `2`:
node_match(call,
mutate(x, ...) ~ 1,
mutate(., ...) ~ 2
)
# Wildcards also apply to names:
node_match(call,
mutate(., weight^2) ~ 1,
mutate(., wrong = weight^2) ~ 2,
mutate(., . = weight^2) ~ 3
)
# If you want to match an argument regardless of whether it has a
# name, use the ellipsis wildcard instead:
node_match(quote(call(arg)),
call(. = arg) ~ 1,
call(... = arg) ~ 2
)
# The RHS is a handy way of providing custom error messages:
fail_unnamed <- function() {
stop("You should provide a named argument")
}
check_sq_suffix <- function(nm) {
if (!grepl(".*_sq$", nm)) {
stop("The new variable must end with `_sq`")
}
message("Alright!")
}
node_match(call,
mutate(., .) ~ fail_unnamed(),
mutate(., `.(nm)` = .^2) ~ check_sq_suffix(nm),
. ~ message("Try again")
)
|
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.