[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