I wish I knew how to reduce this example, but I donβt understand the problem enough to do this.
I have a package that rewrites R functions to make them tail recursive: tailr . He analyzes the recursive function a bit and then translates it into a loop function. For example, it converts this factorial function
factorial <- function(n, acc) { if (n <= 1) acc else factorial(n - 1, acc * n) }
into this version
factorial <- function(n, acc) { .tailr_n <- n .tailr_acc <- acc callCC(function(escape) { repeat { n <- .tailr_n acc <- .tailr_acc if (n <= 1) escape(acc) else { .tailr_n <<- n - 1 .tailr_acc <<- acc * n } } }) }
The generated function is not very good, but it works.
My problem is that I am writing a package that uses a transformation that contains only these lines from R:
running devtools::check() enter this error:
Error in attr(e, "srcref")[[i]] : subscript out of bounds Calls: <Anonymous> ... <Anonymous> -> collectUsage -> collectUsageFun -> walkCode -> h Execution halted
If I put a dummy version of the conversion in the package, I do not receive an error message
dummy_transform_body <- function(expr) { rlang::expr({ .tailr_n <- n .tailr_acc <- acc callCC(function(escape) { repeat { n <- .tailr_n acc <- .tailr_acc if (n <= 1) escape(acc) else { .tailr_n <<- n - 1 .tailr_acc <<- acc * n } } }) }) } dummy_transform <- function(fun) { fun_q <- rlang::enquo(fun) new_fun_body <- dummy_transform_body(body(fun)) result <- rlang::new_function( args = formals(fun), body = new_fun_body, env = rlang::get_env(fun_q) ) result }
I do not see the difference between the two functions, so I am puzzled why the check accepts a dummy, but not a real version.
> body(factorial_loop) == body(factorial_loop_dummy) [1] TRUE > environment(factorial_loop) <environment: namespace:Test> > environment(factorial_loop_dummy) <environment: namespace:Test> > formals(factorial_loop) $n $acc > formals(factorial_loop_dummy) $n $acc > attributes(factorial_loop()) Error in factorial_loop() : argument "n" is missing, with no default > attributes(factorial_loop) NULL > attributes(factorial_loop_dummy) NULL
The error mentions the srcref attribute, but not one of the converted functions has any attributes. If I explicitly set the srcref attribute, this does not help with the error.
Any ideas anyone?
Update 2018/03/20:
The problem seems to be related to quasi-cyclic splicing in my transform function. If I uncomment it, instructions !!! below and manually insert the cases for factorial , then the error will disappear.
dummy_transform_body <- function(fun_expr, info) { vars <- names(formals(info$fun)) tmp_assignments <- vector("list", length = length(vars)) locals_assignments <- vector("list", length = length(vars)) for (i in seq_along(vars)) { local_var <- as.symbol(vars[[i]]) tmp_var <- parse(text = paste(".tailr_", vars[[i]], sep = ""))[[1]] tmp_assignments[[i]] <- rlang::expr(rlang::UQ(tmp_var) <- rlang::UQ(local_var)) locals_assignments[[i]] <- rlang::expr(rlang::UQ(local_var) <- rlang::UQ(tmp_var)) }
Another update:
... Removed the previous update ... Hacking with placing splicing inside another box no longer works for me ...
Another update ...
Ok, I still don't know why splicing doesn't work. I made other dummy functions where it was. So I really wonder if anyone has any ideas. In any case, I managed to rewrite my tailr function to avoid !!! , and now it works.
repeat_body <- as.call( c(`{`, locals_assignments, fun_expr, quote(next)) ) call_cc_stmt <- rlang::expr( callCC(function(escape) { repeat { !!repeat_body } }) ) as.call( c(`{`, tmp_assignments, call_cc_stmt) )
It is much less elegant and the generated code is uglier - but I hide it by installing srcref in the source code, so no one should ever know.