kbare at andrew.cmu.edu
2009-Jul-29 17:50 UTC
[Rd] Strange Interaction Between Promises and Closures (PR#13861)
Full_Name: Keith Bare Version: 2.7.1 OS: Linux Submission from: (NULL) (128.2.134.48) I observed unexpected behavior attempting to use lapply to vary parameters in generated closures. All the generated closures ran with the last parameter value in the list. Here's a simple example:> funcs <- lapply(c("alpha", "beta", "gamma", "delta"), function(x) function()print(x))> funcs[[1]]()[1] "delta"> funcs[[2]]()[1] "delta"> funcs[[3]]()[1] "delta"> funcs[[4]]()[1] "delta" What appears to be happening, is that the unevaluated promise for x is getting stored in the generated closure. This promise references a variable in the local environment for the lapply call. However, that variable gets clobbered by subsequent processing by lapply. This may be a language "feature" rather than a bug. But IMO, the observed behavior is very non-intuitive. If so, maybe it deserves mention in the documentation for lapply, "function", or the language reference section on functions. For now, I've found that adding a force in the function that generates the returned closure is a workaround. E.g.:> funcs <- lapply(c("alpha", "beta", "gamma", "delta"), function(x) { force(x);function() print(x) })> funcs[[1]]()[1] "alpha"> funcs[[2]]()[1] "beta"> funcs[[3]]()[1] "gamma"> funcs[[4]]()[1] "delta" And here's my version output (I'm running the version in Debian lenny):> version_ platform x86_64-pc-linux-gnu arch x86_64 os linux-gnu system x86_64, linux-gnu status major 2 minor 7.1 year 2008 month 06 day 23 svn rev 45970 language R version.string R version 2.7.1 (2008-06-23) --Keith
Peter Dalgaard
2009-Jul-30 09:12 UTC
[Rd] Strange Interaction Between Promises and Closures (PR#13861)
kbare at andrew.cmu.edu wrote:> Full_Name: Keith Bare > Version: 2.7.1 > OS: Linux > Submission from: (NULL) (128.2.134.48) > > > I observed unexpected behavior attempting to use lapply to vary parameters in > generated closures. All the generated closures ran with the last parameter > value in the list. > > Here's a simple example: > >> funcs <- lapply(c("alpha", "beta", "gamma", "delta"), function(x) function() > print(x)) >> funcs[[1]]() > [1] "delta" >> funcs[[2]]() > [1] "delta" >> funcs[[3]]() > [1] "delta" >> funcs[[4]]() > [1] "delta" > > > What appears to be happening, is that the unevaluated promise for x is getting > stored in the generated closure. This promise references a variable in the > local environment for the lapply call. However, that variable gets clobbered by > subsequent processing by lapply. > > > This may be a language "feature" rather than a bug. But IMO, the observed > behavior is very non-intuitive. If so, maybe it deserves mention in the > documentation for lapply, "function", or the language reference section on > functions. > > > For now, I've found that adding a force in the function that generates the > returned closure is a workaround. E.g.: > >> funcs <- lapply(c("alpha", "beta", "gamma", "delta"), function(x) { force(x); > function() print(x) }) >> funcs[[1]]() > [1] "alpha" >> funcs[[2]]() > [1] "beta" >> funcs[[3]]() > [1] "gamma" >> funcs[[4]]() > [1] "delta"Not a bug, consequence of lazy evaluation, and force() IS the workaround. It is not specific to lapply; a prototypical example goes > f <- function(x) function() x > x <- 2 > g <- f(x) > x <- 4 > g() [1] 4 > x <- 6 > g() [1] 4 In the lapply context, the promise refers to an internal loop variable, and if the promise is not evaluated until the loop is done, you get the value of the loop variable at the end. Documenting this sort of thing in a place that actually gets seen is an interesting problem... Probably, Section 4.3.3 in the R-lang manual is the only viable option. -- O__ ---- Peter Dalgaard ?ster Farimagsgade 5, Entr.B c/ /'_ --- Dept. of Biostatistics PO Box 2099, 1014 Cph. K (*) \(*) -- University of Copenhagen Denmark Ph: (+45) 35327918 ~~~~~~~~~~ - (p.dalgaard at biostat.ku.dk) FAX: (+45) 35327907