17.6 Customising evaluation with functions
The above example used an environment that bound x
and y
to vectors. It’s less obvious that you also bind names to functions, allowing you to override the behaviour of existing functions. This is a big idea that we’ll come back to in Chapter 21 where I explore generating HTML and LaTeX from R. The example below gives you a taste of the power. Here I evaluate code in a special environment where *
and +
have been overridden to work with strings instead of numbers:
function(x) {
string_math <- env(
e <-caller_env(),
`+` = function(x, y) paste0(x, y),
`*` = function(x, y) strrep(x, y)
)
eval(enexpr(x), e)
}
"Hadley"
name <-string_math("Hello " + name)
#> [1] "Hello Hadley"
string_math(("x" * 2 + "-y") * 3)
#> [1] "xx-yxx-yxx-y"
dplyr takes this idea to the extreme, running code in an environment that generates SQL for execution in a remote database:
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
DBI::dbConnect(RSQLite::SQLite(), filename = ":memory:")
con <- copy_to(con, mtcars)
mtcars_db <-
%>%
mtcars_db filter(cyl > 2) %>%
select(mpg:hp) %>%
head(10) %>%
show_query()
#> <SQL>
#> SELECT `mpg`, `cyl`, `disp`, `hp`
#> FROM `mtcars`
#> WHERE (`cyl` > 2.0)
#> LIMIT 10
::dbDisconnect(con) DBI