evaluate icon indicating copy to clipboard operation
evaluate copied to clipboard

Recursive evaluate

Open jeroen opened this issue 11 years ago • 6 comments

I am experiencing some strange problems with knitr inside opencpu sometimes which I suspect might be a side effect of the recursive use of evaluate (both by knitr and by opencpu). E.g. a toy example:

evaluate(quote(evaluate(quote(plot(iris))))

Which parts of evaluate might be sensitive to recursion problem? I am mostly thinking about the plot hooks, but perhaps other things as well? @yihui, have you ever experienced problems when trying to call knit inside evaluate, i.e. evaluate("knit(...)")?

jeroen avatar May 28 '13 02:05 jeroen

I have never called evaluate that way, so I do not know.

yihui avatar May 28 '13 04:05 yihui

Hi, I stumbled on that issue tracking a a bug with knit_child, doing plot in a for loop: basically only the last plot was produced.

The problem is due to the recursive evaluate calls: first call by knit() for the R chunk on the parent document (issuing a knit_child), and the second call by the knit child, calling knit then evaluate again. I realized that some (plot) hooks installed by the first evaluate were running during the second evaluate call. So the fix is quite simple: change the default argument of evaluate:::set_hooks to "replace" instead of "append". But there will be side-effects.

What I humbly suggest would be:

  1. to add an option to evaluate (e.g. replace_hooks = FALSE), forwarded to evaluate_call, then used for the set_hooks call.
  2. to modify knitr:::block_exec() so that when child_mode is TRUE, call evaluate with replace_hooks = TRUE

What do you think ?

P.S About the knit_child bug, note that if you do not put the plot statements in a block (e.g. a if, for or {}) it works, because evaluate is called on each separate statement, because of the parse() results

In the meantime I setup a fix that automatically patches evaluate:::set_hooks when in a knit_child.

kforner avatar Jul 31 '14 15:07 kforner

Somewhat related: a while back I suggested to Paul Murrell that graphic events should better be implemented as conditions rather than hooks. That way we can use the standard withCallingHandlers mechanics to catch the graphic, and pass the recorded plot object directly as an argument to the handler. The advantage of conditions over global hooks is that it supports nested evaluations which are currently problematic.

withCallingHandlers(plot(cars), grid.newpage = function(myplot){
   #deal with recordedPlot object here
})

He seemed open to the idea, but not sure how hard it would be to implement this.

jeroen avatar Jul 31 '14 15:07 jeroen

@kforner That sounds good. Do you mind submitting two pull requests to this repo and the knitr repo, respectively? Thanks!

yihui avatar Aug 02 '14 05:08 yihui

Hmm in fact it is more complex that I anticipated, it is difficult to cover all use cases. But I could fix the knitr use case directly in knitr without modifying evaluate. The only change that maybe useful would be to export evaluate:::set_hooks.

kforner avatar Aug 19 '14 15:08 kforner

As for the knit_child bug, last commit (https://github.com/hadley/evaluate/commit/ba0efa53e7c77c20be7558ef726aaa0973799461) by Yihuie seems to fix the problem.

kforner avatar Sep 02 '14 09:09 kforner

Closing, since it's been ~10 years (😱) without further discussion.

hadley avatar Jun 14 '24 18:06 hadley