Question
I have a function f that computes a summary of the environment in which it is called. In this trivial example, it simply sums up all the objects found.
f <- function(){ x <- ls(parent.frame()) sum(sapply(x, get, envir=parent.frame())) } g <- function(x = 7, y){ z <- 3 f() }
However, if called from a function with missing arguments, it throws an error.
R> g(y = 34) [1] 44 R> g() Error in FUN(c("x", "y", "z")[[2L]], ...) : argument "y" is missing, with no default
To handle this, I need a method to indicate from f if y or some other arbitrary object in g is the argument of g , in which case if it is absent.
My attempts so far
To try different solutions, I do
debug(f) g()
Of course, missing(y) does not work, since y not an argument for f . Changing the environment in which missing is evaluated also does not work, since we are still at the same level in the call stack:
Browse[2]> eval(missing(y), parent.frame()) Error in missing(y) : 'missing' can only be used for arguments Browse[2]> identical(sys.frames(), eval(sys.frames(), parent.frame())) [1] TRUE
What I can do is determine if y argument to g using a dirty hack
Browse[2]> eval(substitute(missing(a), list(a="x")), parent.frame()) [1] TRUE Browse[2]> eval(substitute(missing(a), list(a="y")), parent.frame()) [1] TRUE Browse[2]> eval(substitute(missing(a), list(a="z")), parent.frame()) [1] FALSE
which gives TRUE for both x and y , but not for the regular z variable. Combining it with tryCatch , which checks if the argument can be restored, will solve the problem, but it's terribly dirty:
is.argument <- eval(substitute(missing(a), list(a="y")), parent.frame()) if(is.argument){ tryCatch({ get("y", parent.frame()) FALSE }, error = function(e) TRUE) } else { NA }
Moreover, I cannot figure out how to define is.argument for an arbitrary argument, unlike the explicitly specified "y" in the above example.
Update: Goal
In reality, the goal of f is to debug g at runtime. I could call
R> debug(g) R> g()
go through it and check the state of the objects with f , or I can set options(error=recover) and just find the debug g if there is an error. In both cases, there should be a well-defined call stack, so I assume that my main question is whether it can be requested at different levels, similar to the frame stack (accessed via sys.frames() ). I must admit that for me it is deep waters.
Think of f as my own modified version of ls.str , which can be used as follows:
Browse[2]> ls.str() # Inside g() x : num 7 y : <missing>
After some digging into ls.str and utils:::print.ls_str I found out that it performs the same task
for (nam in x) { cat(nam, ": ") o <- tryCatch(get(nam, envir = E, mode = M), error = function(e) e) if (inherits(o, "error")) { cat(if (length(grep("missing|not found", o$message))) "<missing>" else o$message, "\n", sep = "") } else { strO <- function(...) str(o, ...) do.call(strO, strargs, quote = is.call(o) || is.symbol(o)) } }
If there is a right way to do this, I will just do a similar hack.