[Rd] callCC in 2.7.0

Gabor Grothendieck ggrothendieck at gmail.com
Sun Mar 30 22:31:22 CEST 2008


Thanks.  So its intended to jump straight out of deeply nested
calls without having to manage the unwinding.

On Sun, Mar 30, 2008 at 4:22 PM, Luke Tierney <luke at stat.uiowa.edu> wrote:
> On Sun, 30 Mar 2008, Gabor Grothendieck wrote:
>
> > Sorry it should be as follows:
> >
> > fib <- function(i, a = 0, b = 1) {
> >  if (i == 0) b else fib(i-1, b, a+b)
> > }
> >
> > Now, how do we transform that to use callCC?
> >
> >
> > On Sun, Mar 30, 2008 at 1:42 PM, Gabor Grothendieck
> > <ggrothendieck at gmail.com> wrote:
> >> OK. Can you show code to implement the tail recursive version of
> >> fib using callCC in R, say.
> >>
> >> Here it is transformed to tail recursive style:
> >>
> >> fib <- function(i, a = 0, b = 1) {
> >>  if (i == 0) a else fib(i-1, b, a+b)
> >>
> >> Now, how do I add callCC to all this so that the fib call
> >> presumably does not create a new stack instance?
>
> What makes you think callCC has anything to contribute here?
>
> The Wikipedia article Hadley cited says that using CPS effectively is
> difficult without tail call optimization, which R does not have and
> can't, at least not without some restrictions, because the stack is
> avialable via sys.xyz functions.  It does _not_ say that having
> explicit continuations implies having tail call optimization.
>
> If you want to, you can rewrite your fib in CPS, something like
>
>     fibCPS <- function(k, i, a = 0, b = 1) {
>       if (i == 0) k(b) else fibCPS(k, i-1, b, a+b)
>     }
>
> and you can then use callCC to bridge between the nonCPS and CPS
> world, e.g. with
>
>     fib <- function(i) callCC(function(k) fibCPS(k, i, 0, 1))
>
> This will grow the stack just like any other recursion in R.  A
> (minor) difference compared to your original fib is that the final
> exit happens in a single jump rather than a sequence of returns.
>
> The point of adding the current downward-only callCC to R is to
> provide a clean, lexically scoped non-local exit mechanism for exiting
> from complex, possibly recursive function calls. Dylan provides this
> as part of its 'block' construct (also as an exit or continuation
> function); Common Lisp has 'block'/'return-from' constructs that use
> lexically scoped block name symbols.
>
> For a slightly more realistic example than the ones currently in the
> help page: Here is some code that implements a simple binary tree data
> structure, and a function that visits the nodes of the tree and calls
> a specified function on the value in each node:
>
>     mkTree <- function(value, left, right)
>        list(isLeaf = FALSE, value = value, left = left, right = right)
>     mkLeaf <- function(value) list(isLeaf = TRUE, value = value)
>
>     visit <- function(node, fun) {
>        if (node$isLeaf) fun(node$value)
>        else {
>            visit(node$left, fun)
>            visit(node$right, fun)
>            fun(node$value)
>        }
>     }
>
> A simple example tree:
>
>     x <- mkTree(1, mkTree(2, mkLeaf(3), mkLeaf(4)),
>                    mkTree(5, mkLeaf(6), mkLeaf(7)))
>
> You can use visit() to print out the node values with
>
>     > visit(x, print)
>     [1] 3
>     [1] 4
>     [1] 2
>     [1] 6
>     [1] 7
>     [1] 5
>     [1] 1
>     >
>
> If you want to use a function like visit() to traverse the tree but
> want the traversal to stop when some condition is met then you can
> either rewrite visit to allow for this, making it more complicated, or
> you can use a non-local exit.  That is where callCC comes in.  To
> print all values until you find one equal to 7 you can use
>
>     > callCC(function(exit) {
>     +     fun <- function(value) {
>     +         if (value == 7) exit(NULL)
>     +         else print(value)
>     +     }
>     +     visit(x, fun)
>     + })
>     [1] 3
>     [1] 4
>     [1] 2
>     [1] 6
>     NULL
>     >
>
> One can also imagine situations where this might be useful in a
> function passed to an optimizer like optim.  Given that our 'break'
> only breaks out of one loop level, callCC can also be useful for
> breaking out of a set of nested loops.
>
> I first wrote this particular version of callCC when prototyping the
> tryCatch mechanism in pure R code where one needs a means to jump from
> a point where a condition is signaled to the point where the handler
> is established.  The current tryCatch implementation does things
> differently because it needs to integrate with the error handling at
> the C level. Currently I use callCC in the constantFold function in
> codetools (which is why callCC was added when codetools became
> recommended).  This use is similar to the tree example.
>
>
> luke
>
> >>
> >>
> >> On Sun, Mar 30, 2008 at 1:31 PM, Luke Tierney <luke at stat.uiowa.edu> wrote:
> >>> On Sun, 30 Mar 2008, Gabor Grothendieck wrote:
> >>>
> >>>> I think the only relationship to that is the name since
> >>>> it does not appear to allow one to leave a function
> >>>> in the middle of its processing and re-enter it back
> >>>> at that point -- which is what would be needed.
> >>>
> >>> The article conflates basic CPS with having first class continuations
> >>> as in Scheme. The discussion about compilers and tail calls only
> >>> requires downward-only continuations of the kind provided by R's
> >>> current callCC.  The user interface and coroutine discussion requires
> >>> continuations that can be run outside of their creating context.  The
> >>> most sophisticated variant, as provided in Scheme, also allows
> >>> continuations to be run more than once.  I don't think any of the
> >>> examples in the Wikipedia article need that, but there is some
> >>> interesting work on using that to model web browsing behavior.
> >>>
> >>> At any rate, there is plenty of precedent for using callCC as the name
> >>> for the construct here even when the continuation is no longer valid
> >>> outside of the creating callCC call. So the relationship is more than
> >>> just the name.
> >>>
> >>> luke
> >>>
> >>>>
> >>>> On Sun, Mar 30, 2008 at 12:04 PM,  <h.wickham at gmail.com> wrote:
> >>>>>
> >>>>>> Would anyone like to explain if callCC in R 2.7.0 gives
> >>>>>> anything that on.exit does not already provide?
> >>>>>>
> >>>>>> It seems that the exit condition once defined cannot
> >>>>>> be added to overridden whereas with on.exit multiple
> >>>>>> on.exit's add additional on.exits rather than being ignored.
> >>>>>>
> >>>>>> Is this important?
> >>>>>
> >>>>> It facilitates a completely different style of programming - see
> >>>>> http://en.wikipedia.org/wiki/Continuation-passing_style
> >>>>>
> >>>>> --
> >>>>> http://had.co.nz/
> >>>>>
> >>>>
> >>>
> >>>> ______________________________________________
> >>>> R-devel at r-project.org mailing list
> >>>> https://stat.ethz.ch/mailman/listinfo/r-devel
> >>>>
> >>>
> >>> --
> >>> Luke Tierney
> >>> Chair, Statistics and Actuarial Science
> >>> Ralph E. Wareham Professor of Mathematical Sciences
> >>> University of Iowa                  Phone:             319-335-3386
> >>> Department of Statistics and        Fax:               319-335-3017
> >>>    Actuarial Science
> >>> 241 Schaeffer Hall                  email:      luke at stat.uiowa.edu
> >>> Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
> >>>
> >>
> >
>
> --
>
> Luke Tierney
> Chair, Statistics and Actuarial Science
> Ralph E. Wareham Professor of Mathematical Sciences
> University of Iowa                  Phone:             319-335-3386
> Department of Statistics and        Fax:               319-335-3017
>    Actuarial Science
> 241 Schaeffer Hall                  email:      luke at stat.uiowa.edu
> Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
>



More information about the R-devel mailing list