Peter Meilstrup
2013-Aug-18 13:12 UTC
[Rd] How does R_UnboundValue and removing variables work?
Reading "R Internals" made me believe that R_UnboundValue was a placeholder that would be skipped over in variable lookup. viz. the section of R Internals "Hash tables" says "items are not actually deleted but have their value set to R_UnboundValue.", which seems to align with what I read in envir.c. So, I reasoned, if I have a function that returns R_UnboundValue, like so: unbound_value <- function() .Call("unbound_value") SEXP unbound_value() { return R_UnboundValue; } then calling x <- unbound_value() ought to make "x" unbound. [spare me from saying this is a bad idea--I'm just trying to understand what's going on.] But it seems to only work partially -- If a name "x" is bound to R_UnboundValue, exists("x") returns TRUE, though sometimes name lookup proceeds as you'd expect:> x <- 5; local({x <- 6; rm("x"); exists("x", inherits=FALSE)})[1] FALSE> x <-5; local({x <- 6; x <- unbound_value(); exists("x", inherits=FALSE)})[1] TRUE> x <-5; local({x <- 6; x <- vadr:::unbound_value(); x})[1] 5 but assigning unbound on the global namespace blocks name lookup from going up the search path:> find("HairEyeColor")[1] "package:datasets"> HairEyeColor <- unbound_value() > class(HairEyeColor)Error: object 'HairEyeColor' not found> rm("HairEyeColor") > class(HairEyeColor)[1] "table" I've been looking through the source in envir.c but I haven't found what would make rm("x") have a different effect from x <- unbound_value(). Any hints? Peter [[alternative HTML version deleted]]
Simon Urbanek
2013-Aug-19 04:38 UTC
[Rd] How does R_UnboundValue and removing variables work?
Peter, On Aug 18, 2013, at 9:12 AM, Peter Meilstrup wrote:> Reading "R Internals" made me believe that R_UnboundValue was a placeholder > that would be skipped over in variable lookup. viz. the section of R > Internals "Hash tables" says "items are not actually deleted but have their > value set to R_UnboundValue.", which seems to align with what I read in > envir.c. >Not quite. The CAR of the LISTSXP corresponding to the symbol entry is set to R_UnboundValue in case it is cached, but the entry is actually removed from the chain (see RemoveFromList).> So, I reasoned, if I have a function that returns R_UnboundValue, > like so: > > unbound_value <- function() .Call("unbound_value") > > SEXP unbound_value() { > return R_UnboundValue; > } > > then calling > > x <- unbound_value() > > ought to make "x" unbound. [spare me from saying this is a bad idea--I'm > just trying to understand what's going on.] > > But it seems to only work partially -- If a name "x" is bound to > R_UnboundValue, exists("x") returns TRUE, though sometimes name lookup > proceeds as you'd expect: > >> x <- 5; local({x <- 6; rm("x"); exists("x", inherits=FALSE)}) > [1] FALSE >> x <-5; local({x <- 6; x <- unbound_value(); exists("x", inherits=FALSE)}) > [1] TRUE >> x <-5; local({x <- 6; x <- vadr:::unbound_value(); x}) > [1] 5 > > but assigning unbound on the global namespace blocks name lookup from going > up the search path: > >> find("HairEyeColor") > [1] "package:datasets" >> HairEyeColor <- unbound_value() >> class(HairEyeColor) > Error: object 'HairEyeColor' not found >> rm("HairEyeColor") >> class(HairEyeColor) > [1] "table" > > I've been looking through the source in envir.c but I haven't found what > would make rm("x") have a different effect from x <- unbound_value(). Any > hints? >See above - rm() actually removes the entry whereas you are setting it to R_UnboundValue which is really bad (R_UnboundValue should really never leave the internals). Note that exists() is a special case - it only looks if the entry exists in the list, not what its value is, therefore it will find you entry with R_UnboundValue and say, ok, it exists. You can easily see that when you look at the contents of the environment:> e=new.env(FALSE) > e$x=1L > .Internal(inspect(e))@100c6ab18 04 ENVSXP g0c0 [NAM(1)] <0x100c6ab18> FRAME: @100b6f2b0 02 LISTSXP g0c0 [] TAG: @1008a4b10 01 SYMSXP g1c0 [MARK,NAM(2)] "x" @100b6ab38 13 INTSXP g0c1 [NAM(2)] (len=1, tl=0) 1 ENCLOS: @1008745f8 04 ENVSXP g1c0 [MARK,NAM(2),GL,gp=0x8000] <R_GlobalEnv>> e$x=unbound_value() > .Internal(inspect(e))@100c6ab18 04 ENVSXP g0c0 [MARK,NAM(1)] <0x100c6ab18> FRAME: @100b6f2b0 02 LISTSXP g0c0 [MARK] TAG: @1008a4b10 01 SYMSXP g1c0 [MARK,NAM(2)] "x" @100843140 01 SYMSXP g1c0 [MARK,NAM(2)] "x1?" ENCLOS: @1008745f8 04 ENVSXP g1c0 [MARK,NAM(2),GL,gp=0x8000] <R_GlobalEnv>> exists("x",,e)[1] TRUE> rm(x,envir=e) > exists("x",,e)[1] FALSE> .Internal(inspect(e))@100c6ab18 04 ENVSXP g0c0 [MARK,NAM(2)] <0x100c6ab18> ENCLOS: @1008745f8 04 ENVSXP g1c0 [MARK,NAM(2),GL,gp=0x8000] <R_GlobalEnv> You can repeat the same trick with hashed envs - the only difference is that there are then multiple lists and only one is involved. Cheers, S