delayedAssign changing values
On Apr 27, 2012, at 00:10 , ghostwheel wrote:
Simon Urbanek wrote
More intuitive would have been the behavior
delayedAssign("x", local({y <- 7; y+3}) )
which only changes x.
That is questionable - I think it is more logical for both environments to be the same as default. Just think if it -- the point here is to access lazy evaluation which is exactly what it does - lazy evaluation takes place in the original environment, not in another one.
I think I finally understand. My intuition just came from looking at ?delayedAssign. But delayedAssign came to replace delay(), which "creates a promise to evaluate the given expression". When one thinks of delay(), what you said makes sense, you just delay executing a certain expression in the parent frame. I think, though, that with the current way it is described and called, delayedAssign should by default only have the side effect of changing the variable, i.e. use eval.env=new.env().
That's not possible. It involves evaluating an expression, and there is no limit to what side effect this can have.
The manual states: This function is invoked for its side effect, which is assigning a promise to evaluate value to the variable x. I think that is a nice clear side effect - changing a variable when it is evaluated...like a delayed "<<-". Otherwise it seems to me that delayedAssign could cause debugging nightmares. Luckily, it currently doesn't seem to widely used to cause them....
Just don't do that, then.... However, lazy evaluation _per se_ does cause nightmares, or at least surprising behavior. My favorite one (because it actually involves a relevant piece of statistics) is loglike <- function(x,n) function(p) dbinom(x, n, p, log=TRUE) n <- 10 x <- 7 ll <- loglike(x, n) x <- 1 curve(ll) # max at 0.1 which has the issue that x (and n too) is not evaluated until the ll function is called, at which time it may have been changed from the value it had when ll was created.
But you are right that it might be a bit strange that assign.env and eval.env are different. Maybe that is why there are two different parameters - to make the side effects clearer? I tried to find anywhere uses of delayedAssign which make positive use of side effects other than the assignment, and couldn't find any. Does anyone know of such a use?
They'll have to be rather contrived, but printing is one, and perhaps maintaining a count of function calls could be another.
P.S. the end of ?delayedAssign contains this cryptic code:
e <- (function(x, y = 1, z) environment())(1+2, "y", {cat(" HO! "); pi+2})
(le <- as.list(e)) # evaluates the promises
Which I think is another way to create a promise, other than delayedAssign.
But it is really unclear why it sits there at the bottom of the document.
There should probably be more explanation of what this is....
It's actually the _normal_ way to create a promise, namely binding actual arguments to formal arguments. It is just that some trickery is used in order to make the situation visible. I agree that the example looks a bit out of place, though. Perhaps there ought to be a help page on lazy evaluation and a reference to it? (Any volunteers?)
Peter Dalgaard, Professor, Center for Statistics, Copenhagen Business School Solbjerg Plads 3, 2000 Frederiksberg, Denmark Phone: (+45)38153501 Email: pd.mes at cbs.dk Priv: PDalgd at gmail.com