1#' Bind symbols to objects in an environment 2#' 3#' @description 4#' 5#' These functions create bindings in an environment. The bindings are 6#' supplied through `...` as pairs of names and values or expressions. 7#' `env_bind()` is equivalent to evaluating a `<-` expression within 8#' the given environment. This function should take care of the 9#' majority of use cases but the other variants can be useful for 10#' specific problems. 11#' 12#' - `env_bind()` takes named _values_ which are bound in `.env`. 13#' `env_bind()` is equivalent to [base::assign()]. 14#' 15#' - `env_bind_active()` takes named _functions_ and creates active 16#' bindings in `.env`. This is equivalent to 17#' [base::makeActiveBinding()]. An active binding executes a 18#' function each time it is evaluated. The arguments are passed to 19#' [as_function()] so you can supply formulas instead of functions. 20#' 21#' Remember that functions are scoped in their own environment. 22#' These functions can thus refer to symbols from this enclosure 23#' that are not actually in scope in the dynamic environment where 24#' the active bindings are invoked. This allows creative solutions 25#' to difficult problems (see the implementations of `dplyr::do()` 26#' methods for an example). 27#' 28#' - `env_bind_lazy()` takes named _expressions_. This is equivalent 29#' to [base::delayedAssign()]. The arguments are captured with 30#' [exprs()] (and thus support call-splicing and unquoting) and 31#' assigned to symbols in `.env`. These expressions are not 32#' evaluated immediately but lazily. Once a symbol is evaluated, the 33#' corresponding expression is evaluated in turn and its value is 34#' bound to the symbol (the expressions are thus evaluated only 35#' once, if at all). 36#' 37#' - `%<~%` is a shortcut for `env_bind_lazy()`. It works like `<-` 38#' but the RHS is evaluated lazily. 39#' 40#' 41#' @section Side effects: 42#' 43#' Since environments have reference semantics (see relevant section 44#' in [env()] documentation), modifying the bindings of an environment 45#' produces effects in all other references to that environment. In 46#' other words, `env_bind()` and its variants have side effects. 47#' 48#' Like other side-effecty functions like `par()` and `options()`, 49#' `env_bind()` and variants return the old values invisibly. 50#' 51#' 52#' @section Life cycle: 53#' 54#' Passing an environment wrapper like a formula or a function instead 55#' of an environment is soft-deprecated as of rlang 0.3.0. This 56#' internal genericity was causing confusion (see issue #427). You 57#' should now extract the environment separately before calling these 58#' functions. 59#' 60#' @param .env An environment. 61#' @param ... <[dynamic][dyn-dots]> Named objects (`env_bind()`), 62#' expressions `env_bind_lazy()`, or functions (`env_bind_active()`). 63#' Use [zap()] to remove bindings. 64#' @return The input object `.env`, with its associated environment 65#' modified in place, invisibly. 66#' @seealso [env_poke()] for binding a single element. 67#' @export 68#' @examples 69#' # env_bind() is a programmatic way of assigning values to symbols 70#' # with `<-`. We can add bindings in the current environment: 71#' env_bind(current_env(), foo = "bar") 72#' foo 73#' 74#' # Or modify those bindings: 75#' bar <- "bar" 76#' env_bind(current_env(), bar = "BAR") 77#' bar 78#' 79#' # You can remove bindings by supplying zap sentinels: 80#' env_bind(current_env(), foo = zap()) 81#' try(foo) 82#' 83#' # Unquote-splice a named list of zaps 84#' zaps <- rep_named(c("foo", "bar"), list(zap())) 85#' env_bind(current_env(), !!!zaps) 86#' try(bar) 87#' 88#' # It is most useful to change other environments: 89#' my_env <- env() 90#' env_bind(my_env, foo = "foo") 91#' my_env$foo 92#' 93#' # A useful feature is to splice lists of named values: 94#' vals <- list(a = 10, b = 20) 95#' env_bind(my_env, !!!vals, c = 30) 96#' my_env$b 97#' my_env$c 98#' 99#' # You can also unquote a variable referring to a symbol or a string 100#' # as binding name: 101#' var <- "baz" 102#' env_bind(my_env, !!var := "BAZ") 103#' my_env$baz 104#' 105#' 106#' # The old values of the bindings are returned invisibly: 107#' old <- env_bind(my_env, a = 1, b = 2, baz = "baz") 108#' old 109#' 110#' # You can restore the original environment state by supplying the 111#' # old values back: 112#' env_bind(my_env, !!!old) 113env_bind <- function(.env, ...) { 114 .env <- get_env_retired(.env, "env_bind()") 115 invisible(.Call( 116 rlang_env_bind, 117 env = .env, 118 values = list3(...), 119 needs_old = TRUE, 120 bind_type = "value", 121 eval_env = NULL 122 )) 123} 124 125# Doesn't return list of old bindings for efficiency 126env_bind0 <- function(.env, values) { 127 invisible(.Call( 128 rlang_env_bind, 129 env = .env, 130 values = values, 131 needs_old = FALSE, 132 bind_type = "value", 133 eval_env = NULL 134 )) 135} 136 137#' @rdname env_bind 138#' @param .eval_env The environment where the expressions will be 139#' evaluated when the symbols are forced. 140#' @export 141#' @examples 142#' 143#' # env_bind_lazy() assigns expressions lazily: 144#' env <- env() 145#' env_bind_lazy(env, name = { cat("forced!\n"); "value" }) 146#' 147#' # Referring to the binding will cause evaluation: 148#' env$name 149#' 150#' # But only once, subsequent references yield the final value: 151#' env$name 152#' 153#' # You can unquote expressions: 154#' expr <- quote(message("forced!")) 155#' env_bind_lazy(env, name = !!expr) 156#' env$name 157#' 158#' 159#' # By default the expressions are evaluated in the current 160#' # environment. For instance we can create a local binding and refer 161#' # to it, even though the variable is bound in a different 162#' # environment: 163#' who <- "mickey" 164#' env_bind_lazy(env, name = paste(who, "mouse")) 165#' env$name 166#' 167#' # You can specify another evaluation environment with `.eval_env`: 168#' eval_env <- env(who = "minnie") 169#' env_bind_lazy(env, name = paste(who, "mouse"), .eval_env = eval_env) 170#' env$name 171#' 172#' # Or by unquoting a quosure: 173#' quo <- local({ 174#' who <- "fievel" 175#' quo(paste(who, "mouse")) 176#' }) 177#' env_bind_lazy(env, name = !!quo) 178#' env$name 179env_bind_lazy <- function(.env, ..., .eval_env = caller_env()) { 180 .env <- get_env_retired(.env, "env_bind_lazy()") 181 invisible(.Call( 182 rlang_env_bind, 183 env = .env, 184 values = exprs(...), 185 needs_old = TRUE, 186 bind_type = "lazy", 187 eval_env = .eval_env 188 )) 189} 190#' @rdname env_bind 191#' @export 192#' @examples 193#' 194#' # You can create active bindings with env_bind_active(). Active 195#' # bindings execute a function each time they are evaluated: 196#' fn <- function() { 197#' cat("I have been called\n") 198#' rnorm(1) 199#' } 200#' 201#' env <- env() 202#' env_bind_active(env, symbol = fn) 203#' 204#' # `fn` is executed each time `symbol` is evaluated or retrieved: 205#' env$symbol 206#' env$symbol 207#' eval_bare(quote(symbol), env) 208#' eval_bare(quote(symbol), env) 209#' 210#' # All arguments are passed to as_function() so you can use the 211#' # formula shortcut: 212#' env_bind_active(env, foo = ~ runif(1)) 213#' env$foo 214#' env$foo 215env_bind_active <- function(.env, ...) { 216 .env <- get_env_retired(.env, "env_bind_active()") 217 invisible(.Call( 218 rlang_env_bind, 219 env = .env, 220 values = list3(...), 221 needs_old = TRUE, 222 bind_type = "active", 223 eval_env = caller_env() 224 )) 225} 226#' @rdname env_bind 227#' @param lhs The variable name to which `rhs` will be lazily assigned. 228#' @param rhs An expression lazily evaluated and assigned to `lhs`. 229#' @export 230`%<~%` <- function(lhs, rhs) { 231 env_bind_lazy( 232 env, 233 !!substitute(lhs) := !!substitute(rhs), 234 .eval_env = caller_env() 235 ) 236} 237 238 239#' Temporarily change bindings of an environment 240#' 241#' @description 242#' 243#' * `local_bindings()` temporarily changes bindings in `.env` (which 244#' is by default the caller environment). The bindings are reset to 245#' their original values when the current frame (or an arbitrary one 246#' if you specify `.frame`) goes out of scope. 247#' 248#' * `with_bindings()` evaluates `expr` with temporary bindings. When 249#' `with_bindings()` returns, bindings are reset to their original 250#' values. It is a simple wrapper around `local_bindings()`. 251#' 252#' @inheritParams env_bind 253#' @param ... Pairs of names and values. These dots support splicing 254#' (with value semantics) and name unquoting. 255#' @param .frame The frame environment that determines the scope of 256#' the temporary bindings. When that frame is popped from the call 257#' stack, bindings are switched back to their original values. 258#' @return `local_bindings()` returns the values of old bindings 259#' invisibly; `with_bindings()` returns the value of `expr`. 260#' @export 261#' @examples 262#' foo <- "foo" 263#' bar <- "bar" 264#' 265#' # `foo` will be temporarily rebinded while executing `expr` 266#' with_bindings(paste(foo, bar), foo = "rebinded") 267#' paste(foo, bar) 268local_bindings <- function(..., .env = .frame, .frame = caller_env()) { 269 env <- get_env_retired(.env, "local_bindings()") 270 271 old <- env_bind(env, ...) 272 defer(env_bind0(env, old), envir = .frame) 273 274 invisible(old) 275} 276#' @rdname local_bindings 277#' @param .expr An expression to evaluate with temporary bindings. 278#' @export 279with_bindings <- function(.expr, ..., .env = caller_env()) { 280 env <- get_env_retired(.env, "with_bindings()") 281 local_bindings(..., .env = .env) 282 .expr 283} 284 285#' Remove bindings from an environment 286#' 287#' `env_unbind()` is the complement of [env_bind()]. Like `env_has()`, 288#' it ignores the parent environments of `env` by default. Set 289#' `inherit` to `TRUE` to track down bindings in parent environments. 290#' 291#' @inheritParams get_env 292#' @param nms A character vector of binding names to remove. 293#' @param inherit Whether to look for bindings in the parent 294#' environments. 295#' @return The input object `env` with its associated environment 296#' modified in place, invisibly. 297#' @export 298#' @examples 299#' env <- env(foo = 1, bar = 2) 300#' env_has(env, c("foo", "bar")) 301#' 302#' # Remove bindings with `env_unbind()` 303#' env_unbind(env, c("foo", "bar")) 304#' env_has(env, c("foo", "bar")) 305#' 306#' # With inherit = TRUE, it removes bindings in parent environments 307#' # as well: 308#' parent <- env(empty_env(), foo = 1, bar = 2) 309#' env <- env(parent, foo = "b") 310#' 311#' env_unbind(env, "foo", inherit = TRUE) 312#' env_has(env, c("foo", "bar")) 313#' env_has(env, c("foo", "bar"), inherit = TRUE) 314env_unbind <- function(env = caller_env(), nms, inherit = FALSE) { 315 .Call(rlang_env_unbind, env, nms, inherit) 316 invisible(env) 317} 318 319#' Does an environment have or see bindings? 320#' 321#' `env_has()` is a vectorised predicate that queries whether an 322#' environment owns bindings personally (with `inherit` set to 323#' `FALSE`, the default), or sees them in its own environment or in 324#' any of its parents (with `inherit = TRUE`). 325#' 326#' @inheritParams env_unbind 327#' @param nms A character vector of binding names for which to check 328#' existence. 329#' @return A named logical vector as long as `nms`. 330#' @export 331#' @examples 332#' parent <- child_env(NULL, foo = "foo") 333#' env <- child_env(parent, bar = "bar") 334#' 335#' # env does not own `foo` but sees it in its parent environment: 336#' env_has(env, "foo") 337#' env_has(env, "foo", inherit = TRUE) 338env_has <- function(env = caller_env(), nms, inherit = FALSE) { 339 env <- get_env_retired(env, "env_has()") 340 .Call(rlang_env_has, env, nms, inherit) 341} 342 343#' Get an object in an environment 344#' 345#' `env_get()` extracts an object from an enviroment `env`. By 346#' default, it does not look in the parent environments. 347#' `env_get_list()` extracts multiple objects from an environment into 348#' a named list. 349#' 350#' @inheritParams get_env 351#' @inheritParams env_has 352#' @param nm,nms Names of bindings. `nm` must be a single string. 353#' @param default A default value in case there is no binding for `nm` 354#' in `env`. 355#' @return An object if it exists. Otherwise, throws an error. 356#' @export 357#' @examples 358#' parent <- child_env(NULL, foo = "foo") 359#' env <- child_env(parent, bar = "bar") 360#' 361#' # This throws an error because `foo` is not directly defined in env: 362#' # env_get(env, "foo") 363#' 364#' # However `foo` can be fetched in the parent environment: 365#' env_get(env, "foo", inherit = TRUE) 366#' 367#' # You can also avoid an error by supplying a default value: 368#' env_get(env, "foo", default = "FOO") 369env_get <- function(env = caller_env(), nm, default, inherit = FALSE) { 370 env <- get_env_retired(env, "env_get()") 371 .Call( 372 rlang_env_get, 373 env = env, 374 nm = nm, 375 inherit = inherit, 376 closure_env = environment() 377 ) 378} 379#' @rdname env_get 380#' @export 381env_get_list <- function(env = caller_env(), nms, default, inherit = FALSE) { 382 env <- get_env_retired(env, "env_get_list()") 383 .Call( 384 rlang_env_get_list, 385 env = env, 386 nms = nms, 387 inherit = inherit, 388 closure_env = environment() 389 ) 390} 391 392#' Poke an object in an environment 393#' 394#' `env_poke()` will assign or reassign a binding in `env` if `create` 395#' is `TRUE`. If `create` is `FALSE` and a binding does not already 396#' exists, an error is issued. 397#' 398#' 399#' @details 400#' 401#' If `inherit` is `TRUE`, the parents environments are checked for 402#' an existing binding to reassign. If not found and `create` is 403#' `TRUE`, a new binding is created in `env`. The default value for 404#' `create` is a function of `inherit`: `FALSE` when inheriting, 405#' `TRUE` otherwise. 406#' 407#' This default makes sense because the inheriting case is mostly 408#' for overriding an existing binding. If not found, something 409#' probably went wrong and it is safer to issue an error. Note that 410#' this is different to the base R operator `<<-` which will create 411#' a binding in the global environment instead of the current 412#' environment when no existing binding is found in the parents. 413#' 414#' 415#' @inheritParams env_get 416#' @param value The value for a new binding. 417#' @param create Whether to create a binding if it does not already 418#' exist in the environment. 419#' @return The old value of `nm` or a [zap sentinel][zap] if the 420#' binding did not exist yet. 421#' 422#' @seealso [env_bind()] for binding multiple elements. 423#' @export 424env_poke <- function(env = caller_env(), 425 nm, 426 value, 427 inherit = FALSE, 428 create = !inherit) { 429 env <- get_env_retired(env, "env_poke()") 430 invisible(.Call( 431 rlang_env_poke, 432 env = env, 433 nm = nm, 434 values = value, 435 inherit = inherit, 436 create = create 437 )) 438} 439 440#' Names and numbers of symbols bound in an environment 441#' 442#' `env_names()` returns object names from an enviroment `env` as a 443#' character vector. All names are returned, even those starting with 444#' a dot. `env_length()` returns the number of bindings. 445#' 446#' @section Names of symbols and objects: 447#' 448#' Technically, objects are bound to symbols rather than strings, 449#' since the R interpreter evaluates symbols (see [is_expression()] for a 450#' discussion of symbolic objects versus literal objects). However it 451#' is often more convenient to work with strings. In rlang 452#' terminology, the string corresponding to a symbol is called the 453#' _name_ of the symbol (or by extension the name of an object bound 454#' to a symbol). 455#' 456#' @section Encoding: 457#' 458#' There are deep encoding issues when you convert a string to symbol 459#' and vice versa. Symbols are _always_ in the native encoding. If 460#' that encoding (let's say latin1) cannot support some characters, 461#' these characters are serialised to ASCII. That's why you sometimes 462#' see strings looking like `<U+1234>`, especially if you're running 463#' Windows (as R doesn't support UTF-8 as native encoding on that 464#' platform). 465#' 466#' To alleviate some of the encoding pain, `env_names()` always 467#' returns a UTF-8 character vector (which is fine even on Windows) 468#' with ASCII unicode points translated back to UTF-8. 469#' 470#' @inheritParams get_env 471#' @return A character vector of object names. 472#' @export 473#' @examples 474#' env <- env(a = 1, b = 2) 475#' env_names(env) 476env_names <- function(env) { 477 env <- get_env_retired(env, "env_names()") 478 nms <- names(env) 479 .Call(rlang_unescape_character, nms) 480} 481 482#' @rdname env_names 483#' @export 484env_length <- function(env) { 485 if (!is_environment(env)) { 486 abort("`env` must be an environment") 487 } 488 length(env) 489} 490 491#' Lock or unlock environment bindings 492#' 493#' @description 494#' 495#' \Sexpr[results=rd, stage=render]{rlang:::lifecycle("experimental")} 496#' 497#' Locked environment bindings trigger an error when an attempt is 498#' made to redefine the binding. 499#' 500#' @param env An environment. 501#' @param nms Names of bindings. Defaults to all bindings in `env`. 502#' 503#' @return `env_binding_are_unlocked()` returns a logical vector as 504#' long as `nms` and named after it. `env_binding_lock()` and 505#' `env_binding_unlock()` return the old value of 506#' `env_binding_are_unlocked()` invisibly. 507#' 508#' @seealso [env_lock()] for locking an environment. 509#' 510#' @keywords internal 511#' @export 512#' @examples 513#' # Bindings are unlocked by default: 514#' env <- env(a = "A", b = "B") 515#' env_binding_are_locked(env) 516#' 517#' # But can optionally be locked: 518#' env_binding_lock(env, "a") 519#' env_binding_are_locked(env) 520#' 521#' # If run, the following would now return an error because `a` is locked: 522#' # env_bind(env, a = "foo") 523#' # with_env(env, a <- "bar") 524#' 525#' # Let's unlock it. Note that the return value indicate which 526#' # bindings were locked: 527#' were_locked <- env_binding_unlock(env) 528#' were_locked 529#' 530#' # Now that it is unlocked we can modify it again: 531#' env_bind(env, a = "foo") 532#' with_env(env, a <- "bar") 533#' env$a 534env_binding_lock <- function(env, nms = NULL) { 535 nms <- env_binding_validate_names(env, nms) 536 old <- env_binding_are_locked(env, nms) 537 map(nms, lockBinding, env = env) 538 invisible(old) 539} 540#' @rdname env_binding_lock 541#' @export 542env_binding_unlock <- function(env, nms = NULL) { 543 nms <- env_binding_validate_names(env, nms) 544 old <- env_binding_are_locked(env, nms) 545 map(nms, unlockBinding, env = env) 546 invisible(old) 547} 548#' @rdname env_binding_lock 549#' @export 550env_binding_are_locked <- function(env, nms = NULL) { 551 nms <- env_binding_validate_names(env, nms) 552 set_names(map_lgl(nms, bindingIsLocked, env = env), nms) 553} 554 555#' What kind of environment binding? 556#' 557#' \Sexpr[results=rd, stage=render]{rlang:::lifecycle("experimental")} 558#' 559#' @inheritParams env_binding_lock 560#' 561#' @keywords internal 562#' @return A logical vector as long as `nms` and named after it. 563#' @export 564env_binding_are_active <- function(env, nms = NULL) { 565 env_binding_are_type(env, nms, 2L) 566} 567#' @rdname env_binding_are_active 568#' @export 569env_binding_are_lazy <- function(env, nms = NULL) { 570 env_binding_are_type(env, nms, 1L) 571} 572env_binding_are_type <- function(env, nms, type) { 573 if (!is_environment(env)) { 574 abort("`env` must be an environment.") 575 } 576 nms <- env_binding_validate_names(env, nms) 577 promise <- env_binding_types(env, nms) 578 579 if (is_null(promise)) { 580 promise <- rep(FALSE, length(nms)) 581 } else { 582 promise <- promise == type 583 } 584 set_names(promise, nms) 585} 586 587env_binding_validate_names <- function(env, nms) { 588 if (is_null(nms)) { 589 nms <- env_names(env) 590 } else { 591 if (!is_character(nms)) { 592 abort("`nms` must be a character vector of names") 593 } 594 } 595 nms 596} 597env_binding_types <- function(env, nms = env_names(env)) { 598 .Call(rlang_env_binding_types, env, nms) 599} 600 601env_binding_type_sum <- function(env, nms = NULL) { 602 nms <- env_binding_validate_names(env, nms) 603 604 active <- env_binding_are_active(env, nms) 605 promise <- env_binding_are_lazy(env, nms) 606 other <- !active & !promise 607 608 types <- new_character(length(nms), nms) 609 types[active] <- "active" 610 types[promise] <- "lazy" 611 types[other] <- map_chr(env_get_list(env, nms[other]), rlang_type_sum) 612 613 types 614} 615