r - Assignment to replace value in nonlocal list -


[[<- behaves differently lists , environments when used on non-local objects:

lst = list() env = new.env()  (function () lst[['x']] = 1)() (function () env[['x']] = 1)() 
lst # list()  as.list(env) # $x # [1] 1 

in other words, if target of [[<- environment, modifies (nonlocal) environment, if it’s vector/list, creates new, local object.

i know 2 things:

  1. why difference in behaviour?
  2. is there way of achieving same result lists environments, without using <<-?

regarding (1), i’m aware of multiple differences between lists , environments (in particular, know environments don’t copied) documentation not mention why semantics of [[<- differ between 2 — in particular, why operator operate on different scope. bug? it’s counter-intuitive @ least, , requires non-trivial implementation shenanigans.1

regarding (2), obvious solution of course use <<-:

(function () lst[['x']] <<- 1)() 

however, prefer understanding difference rigorously rather working around them. furthermore, i’ve far used assign instead of <<- , prefer this, allows me greater control on scope of assignment (in particular since can specify inherits = false. <<- voodoo taste.

however, above cannot solved (as far know) using assign because assign works on environments, not lists. in particular, while assign('x', 1, env) works (and same above), assign('x', 1, lst) doesn’t work.


1 elaborate, it’s of course expected r different thing different object types using dynamic dispatch (e.g. via s3). however, not case here (at least not directly): distinction in scope resolution happens before object type of assignment target known — otherwise above operate on global lst, rather creating new local object. internally [[<- has equivalent of:

`[[<-` = function (x, i, value) {     if (exists(x, mode = 'environment', inherits = true))         assign(i, value, pos = x, inherits = false)     else if (exists(x, inherits = false)         internal_assign(x, i, value)     else         assign(x, list(i = value), pos = parent.frame(), inherits = false) } 

the r-language definition (section 2.1.10) says:

unlike other r objects, environments not copied when passed functions or used in assignments.

section "6.3 more on evaluation" gives relevant hint:

notice evaluation in given environment may change environment, in cases involving assignment operator, such as

eval(quote(total <- 0), environment(robert$balance)) # rob rob 

this true when evaluating in lists, original list not change because 1 working on copy.

so, answer first question lists need copied assign them, environments can modified in place (which has huge performance implications).

regarding second question:

if working list, option seems to

  • copy list local scope (using get),
  • assign list,
  • use assign copy modified list original environment.

Comments