Title: | Recursive Quoted Language Expansion |
---|---|
Description: | Expands quoted language by recursively replacing any symbol that points to quoted language with the language it points to. The recursive process continues until only symbols that point to non-language objects remain. The resulting quoted language can then be evaluated normally. This differs from the traditional 'quote'/'eval' pattern because it resolves intermediate language objects that would interfere with evaluation. |
Authors: | Brodie Gaslam [aut, cre] |
Maintainer: | Brodie Gaslam <[email protected]> |
License: | GPL (>=2) |
Version: | 0.1.2 |
Built: | 2024-11-01 11:14:59 UTC |
Source: | https://github.com/brodieg/oshka |
Expands quoted language by recursively replacing any symbol that points to quoted language with the language it points to. The recursive process continues until only symbols that point to non-language objects remain. The resulting quoted language can then be evaluated normally. This differs from the traditional 'quote'/'eval' pattern because it resolves intermediate language objects that would interfere with evaluation.
Finds symbols in quoted R language objects and recursively replaces them with any language objects that those symbols point to. This leads to an expanded language object that can be evaluated. Language objects are objects of type "symbol", "language", or "expression", though only unclassed language is expanded by default.
expand(expr, envir = parent.frame(), enclos = if (is.list(envir) || is.pairlist(envir)) parent.frame() else baseenv(), class.shield = getOption("oshka.class.shield"), name.shield = getOption("oshka.name.shield"))
expand(expr, envir = parent.frame(), enclos = if (is.list(envir) || is.pairlist(envir)) parent.frame() else baseenv(), class.shield = getOption("oshka.class.shield"), name.shield = getOption("oshka.name.shield"))
expr |
an object to be evaluated. See ‘Details’. |
envir |
the |
enclos |
Relevant when |
class.shield |
TRUE, FALSE, or character, determines what portions of quoted language are shielded from expansion. TRUE, the default, means that any any classed language (e.g. formula) will be left unexpanded. If FALSE all language will be expanded, irrespective of class. If character, then any classed objects with classes in the vector will be left unexpanded, and all others will be expanded. |
name.shield |
character names of symbols that should not be expanded,
which by default is |
For more general documentation browseVignettes('oshka')
.
If the input is a language object, that object with all symbols recursively expanded, otherwise the input unchanged.
The expansion can be used to implement programmable Non-Standard Evaluation
(NSE hereafter). Users can create complex quoted language expressions from
simple ones by combining them as they would tokens in standard R expressions.
Then, a programmable NSE aware function can use expand
to turn the quoted
language into usable form. See examples.
During the recursive expansion, symbols are looked up through the search path in the same way as standard R evaluation looks up symbols. One subtlety is that if symbol A expands to a language object B, the symbols in language object B are looked for starting from the environment that A is bound to, not the initial evaluation environment. Expansion stops at symbols that point to non-language objects.
Symbols at the first position in calls (e.g. fun
in fun(x, y)
) are
expanded slightly differently: they will continue to be expanded until an
object of mode "function" is found. This is to follow the semantics of
symbol searches in R where a symbol pointing to a non-function object will
not mask a symbol pointing to a function object when it is used as the name
of the function in a call.
You can prevent expansion on portions of language via shielding. Some language is not expanded by default (see next section).
There are two mechanisms for shielding language from expansion. The first one is to give language a class. This is why formulas are not expanded by default. Be careful though that you do not give a symbol a class as that is bad practice and will become an R runtime error in the future.
The second mechanism is to specify symbol names that should not be expanded. This is easier to specify than the class based mechanism, but it is less precise as it applies to all instances of that name. By default the symbols "::" and ":::" are not expanded. If a function call has a shielded symbol for function name the entire call will be shielded.
See the class.shield
and name.shield
parameters, and examples.
xzw <- uvt <- NULL # make sure not lang objects aaa <- quote(xzw > 3) bbb <- quote(xzw < 10) ccc <- quote(aaa & bbb) expand(ccc) ## You can place list like objects in the search path l <- list(bbb=quote(uvt < 9999)) expand(ccc, l) ## But notice what happens if we use `quote(ccc)` instead of ## just `ccc`. This is because in this case `expand` must ## look for the `ccc` symbol in the search path, and once ## it finds it it looks for `aaa` and `bbb` starting from the ## environment `ccc` is bound to, so the `bbb` defined ## inside `l` is skipped. expand(quote(ccc), l) ## Implementing an NSE fun (see vignettes for detailed ## examples) subset2 <- function(x, subset) { subset <- expand(substitute(subset), x, parent.frame()) eval(bquote(base::subset(.(x), .(subset))), parent.frame()) } subset2(iris, Sepal.Width > 4.3) iris.sub <- quote(Sepal.Width > 4.3) subset2(iris, iris.sub) ## You can shield all instances of a symbol from expansion. ## Note we append existing name shield list. expand(ccc, name.shield=c(getOption('oshka.name.shield'), 'bbb')) ## You can also shield by attaching classes to language ## objects or portions thereof expand(I(ccc)) # add the `AsIs` class to `ccc` with `I` expand(ccc) ## If you wish to shield a symbol with this method you ## cannot do so directly. Note the `quote((bbb))` as ## otherwise we would attach attributes to a symbol: ccd <- bquote(aaa & .(I(quote((bbb))))) expand(ccd) ## Equivalently cce <- ccc cce[[3]] <- I(quote((bbb))) expand(cce) ## Formulas not expanded by default, but can be forced ## to expand by setting `class.shield` to FALSE expand(aaa ~ bbb) expand(aaa ~ bbb, class.shield=FALSE)
xzw <- uvt <- NULL # make sure not lang objects aaa <- quote(xzw > 3) bbb <- quote(xzw < 10) ccc <- quote(aaa & bbb) expand(ccc) ## You can place list like objects in the search path l <- list(bbb=quote(uvt < 9999)) expand(ccc, l) ## But notice what happens if we use `quote(ccc)` instead of ## just `ccc`. This is because in this case `expand` must ## look for the `ccc` symbol in the search path, and once ## it finds it it looks for `aaa` and `bbb` starting from the ## environment `ccc` is bound to, so the `bbb` defined ## inside `l` is skipped. expand(quote(ccc), l) ## Implementing an NSE fun (see vignettes for detailed ## examples) subset2 <- function(x, subset) { subset <- expand(substitute(subset), x, parent.frame()) eval(bquote(base::subset(.(x), .(subset))), parent.frame()) } subset2(iris, Sepal.Width > 4.3) iris.sub <- quote(Sepal.Width > 4.3) subset2(iris, iris.sub) ## You can shield all instances of a symbol from expansion. ## Note we append existing name shield list. expand(ccc, name.shield=c(getOption('oshka.name.shield'), 'bbb')) ## You can also shield by attaching classes to language ## objects or portions thereof expand(I(ccc)) # add the `AsIs` class to `ccc` with `I` expand(ccc) ## If you wish to shield a symbol with this method you ## cannot do so directly. Note the `quote((bbb))` as ## otherwise we would attach attributes to a symbol: ccd <- bquote(aaa & .(I(quote((bbb))))) expand(ccd) ## Equivalently cce <- ccc cce[[3]] <- I(quote((bbb))) expand(cce) ## Formulas not expanded by default, but can be forced ## to expand by setting `class.shield` to FALSE expand(aaa ~ bbb) expand(aaa ~ bbb, class.shield=FALSE)