[Rd] Assignment to string

Wacek Kusnierczyk Waclaw.Marcin.Kusnierczyk at idi.ntnu.no
Wed Apr 1 23:11:26 CEST 2009


Stavros Macrakis wrote:
> The documentation for assignment says:
>
>      In all the assignment operator expressions, 'x' can be a name or
>      an expression defining a part of an object to be replaced (e.g.,
>      'z[[1]]').  A syntactic name does not need to be quoted, though it
>      can be (preferably by backticks).
>
> But the implementation allows assignment to a character string (i.e. not a
> name), which it coerces to a name:
>
>          "foo" <- 23; foo
>          # returns 23
>          > is.name("foo")
>          [1] FALSE
>
> Is this a documentation error or an implementation error?
>   

i think this concords with the documentation in the sense that in an
assignment a string can work as a name.  note that

    `foo bar` = 1
    is.name(`foo`)
    # FALSE

the issue is different here in that in is.name("foo") "foo" evaluates to
a string (it works as a string literal), while in is.name(`foo`) `foo`
evaluates to the value of the variable named 'foo' (with the quotes
*not* belonging to the name).

with only a quick look at the sources (src/main/envir.c:1511), i guess
the first element to an assignment operator (i mean the left-assignment
operators) is converted to a name, so that in

    "foo" <- 1

"foo" evaluates to a string and not a name (hence is.name("foo") is
false), but internally it is sort of 'coerced' to a name, as in

    as.name("foo")
    # `foo`
    is.name(as.name("foo"))
    # TRUE

> The coercion is not happening at parse time:
>
>     class(quote("foo"<-3)[[2]])
>     [1] "character"
>   

i think the internal assignment op really receives a string in a case
like "foo" <- 1, it knows it has to treat it as a name without the
parser classifying the string as a name.  (pure guesswork, again.)

the documentation might avoid calling a plain string a 'quoted name',
though, it is confusing.  a quoted name is something like quote(name) or
quote(`name`):

    is(quote(name))
    # "name" "language"

    is(quote(`name`))
    # "name" "language"

but *not* something like "name":
   
    is("name")
    # "character" "vector" "data.frameRowLabels"

and *not* like quote("name"):
   
    is(quote("name"))
    # "character" "vector" "data.frameRowLabels"


> In fact, bizarrely, not only does it coerce to a name, it actually
> *modifies* the parse tree:
>
>     > gg <- quote("hij" <- 4)
>     > gg
>     "hij" <- 4
>     > eval(gg)
>     > gg
>     hij <- 4
>   

wow!  that's called 'functional programming' ;)
you're right:

    gg = quote({"a" = 1})
    is(gg[[2]][[2]])
    # "character" ...
    eval(gg)
    is(gg[[2]][[2]])
    # "name" ...
  

> *** The cases below only come up with expression trees generated
> programmatically as far as I know, so are much more marginal cases. ***
>
> The <- operator even allows the left-hand-side to be of length > 1, though
> it just ignores the other elements, with the same side effect as before:
>   

that's clear from the sources;  see src/main/envir.c:1521.  it should be
documented (maybe it is, i haven't investigated this issue).

>     > gg <- quote(x<-44)
>     > gg[[2]] <- c("x","y")
>     > gg
>     c("x", "y") <- 44
>   
> eval(gg)

but also this:

    rm(list=ls())
    do.call('=', list(letters, 1))
    # just fine
    a
    # 1
    b
    # error


weird these work.  i think it deserves a warning, at the very least, as in

    c('x', 'y') = 4
    # error: assignment to non-language object
    c(x, y) = 4
    # error: could not find function c<-

(provided that x and y are already there)

btw., that's what you can do with rvalues (using the otherwise
semantically void operator `:=`).

these could seem equivalent, but they're (obviously) not:

    'x' = 1
    c('x') = 1

    x = 1
    c(x) = 1

>     > x
>     [1] 44
>     > y
>     Error: object "y" not found
>     > gg
>     x <- 44
>
> None of this is documented in ? <-, and it is rather a surprise that
> evaluating an expression tree can modify it.  I admit we had a feature
> (performance hack) like this in MacLisp years ago, where expanded syntax
> macros replaced the source code of the macro, but it was a documented,
> general, and optional part of the macro mechanism.
>   

but

- maclisp was designed by computer scientists in a research project,
- r is being implemented by statisticians for practical purposes.

almost every part differs here (and almost no pun intended).

> Another little glitch:
>
>     gg <- quote(x<-44); gg[[2]] <- character(0); eval(gg)
>     Error in eval(expr, envir, enclos) :
>       'getEncChar' must be called on a CHARSXP
>
> This looks like an internal error that users shouldn't see.
>   

by no means the only example that the interface is no blood-brain barrier.

vQ



More information about the R-devel mailing list