Bug 13861 - Strange Interaction Between Promises and Closures
Summary: Strange Interaction Between Promises and Closures
Status: NEW
Alias: None
Product: R
Classification: Unclassified
Component: Low-level (show other bugs)
Version: old
Hardware: All Linux
: P5 normal
Assignee: Jitterbug compatibility account
URL:
Depends on:
Blocks:
 
Reported: 2009-07-29 22:44 UTC by Jitterbug compatibility account
Modified: 2009-07-30 14:12 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jitterbug compatibility account 2009-07-29 22:44:43 UTC
From: kbare@andrew.cmu.edu
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

Comment 1 Jitterbug compatibility account 2009-07-30 13:12:10 UTC
Audit (from Jitterbug):
Thu Jul 30 08:12:10 2009	ripley	moved from incoming to Low-level
Comment 2 Jitterbug compatibility account 2009-07-30 14:12:58 UTC
From: Peter Dalgaard <p.dalgaard@biostat.ku.dk>
kbare@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@biostat.ku.dk)              FAX: (+45) 35327907