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