A colleague recently was looking at call graphs and wanted to see what called what. We sorted that with foodweb from mvbutils, but I was wondering about how best to create a decorator (in python speak) in R. So I did this:
instrument=function(z){ force(z) n=deparse(substitute(z)) # get the name f=function(...){ cat("calling ", n,"\n") x=z(...) cat("done\n") return(x) } return(f) }
This lets me do:
> foo=function(x,y){x+y} > foo(1,2) [1] 3
and now I can make the function log itself by wrapping it:
> foo=instrument(foo) > foo(1,2) calling foo done [1] 3
has this been done before, in a package say, and have I missed any gotchas that will break my way of doing this?
The trace
function in R does that. See ?trace
.
my github package tag attempts to tackle this issue.
Your example could be solved as follows :
# remotes::install_github("moodymudskipper/tag") library(tag) deco <- tag(args = list(.first = NULL, .last = NULL), pattern = { t_args <- T_ARGS() # fetch arguments fed to tag eval.parent(t_args[[".first"]]) # run .first arg on.exit(eval.parent(t_args[[".last"]])) # run .last arg on exit CALL() # run main call }) foo <- function(x, y) {Sys.sleep(1); x + y} # sleep 1 sec to highlight expected behavior deco(quote(message("calling foo")), quote(message("done")))$foo(1, 2) #> calling foo #> done #> [1] 3 foo2 <- deco(quote(message("calling foo")), quote(message("done")))$foo foo2(1, 2) #> calling foo #> done #> [1] 3 deco2 <- deco(quote(message("calling foo")), quote(message("done"))) deco2$foo(1, 2) #> calling foo #> done #> [1] 3
Created on 2020-01-30 by the reprex package (v0.3.0)
tags are function operator factories (or adverb factories), here deco
is a tag, and deco(quote(message("calling foo")), quote(message("done")))
is an adverb, with a method for $
. It means you could run deco(quote(message("calling foo")), quote(message("done")))(foo)(1,2)
, but the dollar notation makes it friendlier.
The tag definition features defaults arguments (default value is mandatory, dots aren't supported), and a pattern
which is a bit like your new body, using special functions T_ARGS()
, F_ARGS()
, F_ARGS()
, F_FORMALS()
and CALL()
to access the tag or function's arguments or formals and the call itself (see ?tag::CALL
).
Some more magic is implemented so the tag's argument can be given to the tagged function itself, so the following can be done too :
deco$foo(1, 2, quote(message("calling foo")), quote(message("done"))) #> calling foo #> done #> [1] 3 foo2 <- deco$foo foo2(1, 2, quote(message("calling foo")), quote(message("done"))) #> calling foo #> done #> [1] 3
In these cases you can enjoy the autocomplete in RStudio :
More info : https://github.com/moodymudskipper/tag The package tags contains a collection of such "decorators"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With