How would you take a tree of nested R lists, and print them in such a way that they can be evaluated as code?
For example, imagine we have the following list code:
spec <- list(
description="An mtcards example",
data = list(),
mark = "point",
encoding = list(
x = list(field = "wt", type="quantitative"),
y = list(field="mpg", type="quantitative"),
color = list(field="cyl", type="nominal")))
Is there a function f
(either in the standard library, or somewhere in CRAN) such that f(spec)
produces a string that looks more or less like the code used to define spec
, ideally with sane indentation based on line lengths? Something like:
'list(
description="An mtcards example",
data = list(),
mark = "point",
encoding = list(
x = list(field = "wt", type="quantitative"),
y = list(field="mpg", type="quantitative"),
color = list(field="cyl", type="nominal")))'
This is possible in other languages, like JS, Python and Clojure. Typically, data structures print out as literals which can be evaluated by the language at hand. There are also special functions for doing this with indentation ("pretty printing", we typically call it). But I don't recall ever coming across something like this in R (though it's been many years since I've used it extensively), and Google has proved useless in turning anything up.
For context, there's consideration right now of producing automatic translations of Vega-Lite/Vega examples in other languages (which is easy for Clojure EDN data representations), but it's not clear how we'd do this for R data specifications as used by vegawidget. Does anyone have a sense of how we might do this, or feel inspired to write the code to do it by hand?
Thanks!
Update: It's been pointed out that deparse
(and dput
) get us pretty close here, but they still don't output the kind of indentation you'd expect to see in real code. In particular, for this example I get:
> dput(spec)
list(description = "An mtcards example", data = list(), mark = "point",
encoding = list(x = list(field = "wt", type = "quantitative"),
y = list(field = "mpg", type = "quantitative"), color = list(
field = "cyl", type = "nominal")))
In particular, I'd expect lines not to be broken in the middle of a list definition unless necessary; color = ...
should be on it's own line here. I'm not thrilled about some other entries in the top level list(...)
being on the same line, and others separate, but we could probably live with that for examples. I would never expect to see the color = ...
list broken across lines like that though in code docs, so this doesn't fully solve the problem, unless someone has a better solution.
Thanks again!
The function dput
is what we would use as a standard way of converting an R object into a string of text that would reproduce that object when run in the R console. In fact, it is very frequently used when posting questions and answers in the R tag on Stack Overflow. For example:
x <- list(a = 1, b = "2")
dput(x)
#> list(a = 1, b = "2")
However, dput
doesn't always give an aesthetically pleasing output. It sounds like what you are looking for is a combination of dput
with a code prettifier.
Base R doesn't have a code prettifier. Apart from anything else, what makes code pretty is quite subjective, and as with most programming languages, the R authors are not prescriptive about the layout of legal code.
However, there are packages available for R that can prettify code, and these can be used on the output of dput
. For example, we could write a function such as this:
f <- function(x) {
text_con <- textConnection("output", "w")
dput(x, text_con)
close(text_con)
formatR::tidy_source(text = output, args.newline = TRUE, width = 40)
}
Which is a sort of prettified dput
:
f(spec)
#> list(
#> description = "An mtcards example", data = list(),
#> mark = "point", encoding = list(
#> x = list(field = "wt", type = "quantitative"),
#> y = list(field = "mpg", type = "quantitative"),
#> color = list(field = "cyl", type = "nominal")
#> )
#> )