4

I'm experimenting with simple manipulation of R code and trying to generate an object equivalent to

substitute(function(x) x)

I am aware I can do something around these lines

as.call(list(as.symbol("function"), as.pairlist(alist(x=)), as.symbol("x")))

but I am looking for a way to achieve as.pairlist(alist(x=)) without resorting alist or, if that's not possible, allowing me to generate equivalent expression without hard-coding the thing or parsing strings.

I was tinkering with things like as.call(list(as.symbol("="), as.symbol("x"))), but that's seems to be a dead end for now.

10465355
  • 4,481
  • 2
  • 20
  • 44
  • 1
    What do you mean by "generate [an] equivalent expression without hard-coding the thing or parsing strings"? It's not really clear what level of generality you are looking for. Do you want to be able to generate an arbitrary named (tagged) pairlist of formals given some names and (optionally) values? – Mikael Jagan Mar 12 '23 at 21:35
  • @MikaelJagan Basically, what you've already guessed ‒ looking for a programmatic way to create `alist`, if getting a `pairlist` directly is not an option. – 10465355 Mar 12 '23 at 22:07

2 Answers2

5

A programmatic alternative to as.pairlist(alist(...)) is to initialize a pairlist of positive length then assign names (well, "tags") and values:

zzz <- vector("pairlist", 1L)
names(zzz) <- "x"
zzz[[1L]] <- substitute()

identical(zzz, as.pairlist(alist(x = )))
## [1] TRUE

More generally:

pl <- function(n, tags, values) {
    r <- vector("pairlist", n)
    if (n >= 1L) {
        names(r) <- tags
        for (i in seq_len(n)) {
            v <- values[[i]]
            if (missing(v) || !is.null(v))
                r[[i]] <- values[[i]]
        }
    }
    r
}

identical(pl(3L, c("x", "y", "z"), list(substitute(), 0, NULL)),
          as.pairlist(alist(x = , y = 0, z = NULL)))
## [1] TRUE

We condition on n >= 1L because a zero-length pairlist is NULL and assigning names or values to NULL is an error.

We condition on missing(v) || !is.null(v) because we don't want to assign NULL to r[[i]] (which would decrease the length of r by 1) and we don't want to call is.null with no argument:

v <- substitute()
missing(v)
## [1] TRUE
is.null(v)
## Error: argument "v" is missing, with no default
Mikael Jagan
  • 9,012
  • 2
  • 17
  • 48
  • Thanks, that looks like what I'm looking for. Is there any reference for this usage of `substitute`? – 10465355 Mar 12 '23 at 22:00
  • 1
    It isn't a documented usage, partly on purpose on the part of R-core, but you'll likely find discussion on one of the mailing lists. (Every few years someone asks about the "missing" symbol.) And of course you can always look at the source code of `substitute`, written in C. – Mikael Jagan Mar 12 '23 at 22:12
2

There isn't much scope for avoiding alist, other than stealing the pairlist from an anonymous function definition using formals:

as.function(c(formals(\(x){}), quote(x)))
#> function (x) 
#> x

Or stealing from a built-in function:

as.function(c(formals(mean)[1], quote(x)))
#> function (x) 
#> x
Allan Cameron
  • 147,086
  • 7
  • 49
  • 87