evaluate
evaluate copied to clipboard
Recursive evaluate
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(...)")
?
I have never called evaluate
that way, so I do not know.
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:
- to add an option to evaluate (e.g. replace_hooks = FALSE), forwarded to evaluate_call, then used for the set_hooks call.
- 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.
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.
@kforner That sounds good. Do you mind submitting two pull requests to this repo and the knitr repo, respectively? Thanks!
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.
As for the knit_child bug, last commit (https://github.com/hadley/evaluate/commit/ba0efa53e7c77c20be7558ef726aaa0973799461) by Yihuie seems to fix the problem.
Closing, since it's been ~10 years (😱) without further discussion.