GlennS
GlennS

Reputation: 5601

How can I decorate a function in R?

I'm trying to instrument some functions in R, by replacing them with my own versions which record some information and then call the original versions. My trouble is, how do I construct the call such that it exactly replicates the original arguments.

First, we'll set up an environment

   env <- new.env()

One way I could do this is by getting the call and modifying it. However, I don't know how to do this with a primitive function.

## Option 1: get the call and modify it
env$`[` <- function(x, i, j, ...) {
    my.call <- match.call()
    ## This isn't allowed.
    my.call[[1]] <- as.name(.Primitive("["))

    record.some.things.about(i, j)

    eval(my.call, envir = parent.frame())
}

Alternatively, I could just get the arguments and use do.call. However, I haven't worked out how to extract the arguments correctly.

## Option 2: do a call with the arguments
env$`[` <- function(x, i, j, ...) {
    ## This outputs a list, so if 'i' is missing, then 'j' ends up in the 'i' position.
    ## If I could get an alist here instead, I could keep 'j' in the right position.
    my.args <- match.call()[-1]

    record.some.things.about(i, j)

    do.call(
        .Primitive("["),
        my.args,
        envir = parent.frame()
    )
}

Then, if we've done things right, we can eval some expression which uses [ inside the enviroment we've constructed, and our instrumented function will be called instead:

## Should return data.frame(b = c(4, 5, 6))
evalq(
    data.frame(a = c(1, 2, 3), b = c(4, 5, 6))[, "b"],
    envir = env
)

Can anyone help/advise?

Upvotes: 1

Views: 222

Answers (2)

Roland
Roland

Reputation: 132706

Use trace:

trace(`[.data.frame`, quote({print("hello!")}), at = 1) 
iris[1,]
#Tracing `[.data.frame`(iris, 1, ) step 1 
#[1] "hello!"
#  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#1          5.1         3.5          1.4         0.2  setosa

Upvotes: 4

Brandon Loudermilk
Brandon Loudermilk

Reputation: 970

Can't you just capture everything for original function args with triple dots arg, that gets passed on to the original function?

sum <- function(..., na.rm = TRUE) {
  print("my sum") # here is where you can "record some info"
  base::sum(..., na.rm = na.rm) # then call original function w/ ...
}

base::sum(5,5, NA)
##[1] NA

# your function
sum(5,5, NA)
##[1] "my sum"
##[1] 10

Upvotes: 0

Related Questions