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