[Rd] How does R_UnboundValue and removing variables work?

Simon Urbanek simon.urbanek at r-project.org
Mon Aug 19 06:38:56 CEST 2013


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



More information about the R-devel mailing list