4

This would seem to be an elementary question, but I can't seem to find an answer on stackoverflow.

How can I obtain the following effect:

f <- function(x = 1){x^2}
miracle(f)
[1]  "x^2"

The context is a shiny app (package by RStudio) in which I have a textInput() function to which I supply an initial value x^2. While this works:

textInput(inputId = "inFun", label = h4("Enter a function:"), value = "x^2")

this doesn't:

textInput(inputId = "inFun", label = h4("Enter a function:"), value = f)

It appears that I need something like "x^2" on the rhs of value.

Below is a representative sample of several variations I have tried:

eval(parse(text = f))
Error in as.character(x) : 
  cannot coerce type 'closure' to vector of type 'character'

f(x = "x")
Error in x^2 : non-numeric argument to binary operator

`f`
function(x){x^2}

f(x = `x`)
Error in f(x = x) : object 'x' not found

Is there a built-in function for this?

2
  • 1
    What about body(f)? Commented Jan 15, 2014 at 9:33
  • Can you adjust the title? Without putting the answer in the title? Commented Jan 15, 2014 at 15:52

1 Answer 1

9

I'd like to answer my own question, based on Roman Luštrik's comment, to invite suggestions for improvements rather than raising my meagre tally of "points".

Roman suggested the function body(), which I had never heard of. Here is what body() does to f:

f <- function(x = 1){x^2}

> body(f)
{
    x^2
}

The curly brackets were unwanted, so I searched a little further. I managed to get rid of the curly brackets with this:

> gsub(' {2,}','',deparse(body(f))[2])
[1] "x^2"

The above, therefore, answers my own question. But is there a more elegant and shorter way?

Following Roman's suggestion to use body(), I came across this outstanding answer by joran, hadley, and several others, which provided me with a template:

How to create an R function programmatically?

There it explains how to create a function programmatically from an argument list, a body and an environment. I therefore decided to construct my function f with these 3 primitives and to call the body from inside shiny's textInput.

So I put this in my global.R file (the small-cap g is shorthand for global)

# Convenience function
make.function <- function(args = alist(a = 1, b = 2), body = quote(a + b), 
                          env = parent.frame()) {
  subs <- list(args = as.pairlist(args), body = body)
  eval(substitute(`function`(args, body), subs), env)
}
gArg <- alist(a = 1, b = 2)
gBody <- quote(a + b)
gFun <- make.function(gArg, gBody)

Then in my server.R file, I have:

textInput(inputId = "inFun", label = h4("1. Enter a function:"), 
          value = deparse(body(gFun)))

And it works!

I was planning to write value = gBody or something to that effect, but my first success came with deparse(body(gFun)), so that's what I'm using now.

The use of make.function to produce a 'static' function in global.R is of course overkill, but I'm using make.function elsewhere inside server.R to process the user-supplied arguments and body to create new functions and plot them, so it's a very useful function to have.

Thanks Roman: if you write your own answer I'll accept yours.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.