Continuation-parsing / trampoline / infinite recursion problem
?
I am not sure I can see exactly how the parameters are changing at all, regardless of which of the versions I am using. Nowhere in the code do I ever modify assign to a variable (except for defining the global-level functions).
I think my problem is that I don?t really understand ... here.
I would expect these two cases, with and without a thunk, to give me the same output, but they clearly do not.
x <- function(...) eval(substitute(alist(...)))
x(a = 2, b = 3)
x(c = 4, d = 5)
xx <- function(...) function() eval(substitute(alist(...)))
xx(a = 2, b = 3)()
xx(c = 4, d = 5)()
The first gives me the parameters and the second just ? back.
How is the thunk actually seeing ... and why does it work with do.call and not with direct call?
library(pryr)
xxx <- function(...) function() do.call(eval %.% substitute %.% alist, list(...))
xxx(a = 2, b = 3)()
xxx(c = 4, d = 5)()
gives me the same results as the xx case, so it is not the do.call that does it, even though that works in my examples.
With
xxxx <- function(...) { list(...) ; function() eval(substitute(alist(...))) }
xxxx(a = 2, b = 3)()
xxxx(c = 4, d = 5)()
it is the same.
Explicitly naming the parameters, of course works fine
y <- function( ...) { params <- list(...) ; function() params }
y(a = 2, b = 3)()
y(c = 4, d = 5)()
Here I get the expected lists out.
I guess I just shouldn?t be using ... in an inner function that refers to the parameters in an outer function. I?m not even sure what that should be expected to do and I certainly do not understand what is happening :)
Explicitly remembering the parameters seems to work fine, though.
Cheers
Thomas
On 10 August 2016 at 19:28:43, Duncan Murdoch (murdoch.duncan at gmail.com(mailto:murdoch.duncan at gmail.com)) wrote:
On 10/08/2016 1:10 PM, Thomas Mailund wrote:
That did the trick! I was so focused on not evaluating the continuation that I completely forgot that the thunk could hold an unevaluated value? now it seems to be working for all the various implementations I have been playing around with. I think I still need to wrap my head around *why* the forced evaluation is necessary there, but I will figure that out when my tired brain has had a little rest.
The original version
make_thunk <- function(f, ...) function() f(?)
says to construct a new function whose body evaluates the expression
f(...). It never evaluates f nor ... , so they don't get evaluated
until the first time you evaluate that new function.
My version containing list(...) forces evaluation of ... . It would
have been even better to use
make_thunk <- function(f, ...) { list(f, ...); function() f(?) }
because that forces evaluation of both arguments.
I suspect you would have problems with
make_thunk <- function(f, ...) function() do.call(f, list(...))
for exactly the same reasons as the original; I'm surprised that you
found it appears to work.
Duncan Murdoch
Thanks a lot! Thomas
On 10 Aug 2016, at 19:04, Duncan Murdoch wrote: On 10/08/2016 12:53 PM, Thomas Mailund wrote:
On 10 Aug 2016, at 13:56, Thomas Mailund wrote: make_thunk <- function(f, ...) f(...)
Doh! It is of course this one: make_thunk <- function(f, ...) function() f(?) It just binds a function call into a thunk so I can delay its evaluation.
I haven't looked closely at the full set of functions, but this comment:
force(continuation) # if I remove this line I get an error
makes it sound as though you're being caught by lazy evaluation. The "make_thunk" doesn't appear to evaluate ..., so its value can change between the time you make the thunk and the time you evaluate it. I think you could force the evaluation within make_thunk by changing it to
make_thunk <- function(f, ...) { list(...); function() f(?) }
and then would be able to skip the force() in your thunk_factorial function.
Duncan Murdoch