Problems checking a package containing generated functions

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:

 #' Computes the factorial. #' @param n A number #' @param acc Accumulator to make the function tail-recursive #' @return factorial of n #' @export factorial <- function(n, acc) { if (n <= 1) acc else factorial(n - 1, acc * n) } #' Computes the factorial. #' @param n A number #' @return factorial of n #' @param acc Accumulator to make the function tail-recursive #' @export factorial_loop <- tailr::loop_transform(factorial) 

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 } #' Computes the factorial. #' @param n A number #' @return factorial of n #' @param acc Accumulator to make the function tail-recursive #' @export factorial_loop_dummy <- dummy_transform(factorial) 

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)) } # this would be a nice pipeline, but it is a bit much to require # magrittr just for this fun_expr <- make_returns_explicit(fun_expr, FALSE, info) fun_expr <- simplify_returns(fun_expr, info) fun_expr <- handle_recursive_returns(fun_expr, info) fun_expr <- returns_to_escapes(fun_expr, info) fun_expr <- simplify_nested_blocks(fun_expr) rlang::expr({ #!!! tmp_assignments .tailr_n <- n .tailr_acc <- acc callCC(function(escape) { repeat { #!!! locals_assignments n <<- .tailr_n acc <<- .tailr_acc !! fun_expr next } }) }) } 

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.

+5
source share

Source: https://habr.com/ru/post/1275958/


All Articles