the following is a trivialized version of some functional code i tried to use in r: (funcs = lapply(1:5, function(i) function() i)) # a list of no-parameter functions, each with its own closure environment, # each supposed to return the corresponding index when applied to no arguments sapply(funcs, function(func) func()) # supposed to return c(1,2,3,4,5) there is absolutely nothing unusual in this code, in the context of functional programming. the following is my best translation to python (modulo indexing, which is inessential), where it does what i wanted: funcs = map(lambda i: lambda: i, range(5)) map(lambda func: func(), funcs) # [0,1,2,3,4] all these functions have distinct environments: (envs = sapply(funcs, function(func) environment(func))) assign("i", 0, environment(envs[[1]])) sapply(funcs, function(func) func()) interestingly, identical says they are all unequal, but compare disagrees. check = function(equal) for (i in 1:4) for (j in i:4+1) print(equal(envs[[i]], envs[[j]])) check(identical) check(compare) compare seems to cast environments to character; for identical, the docs give an example where environments are compared, but compare fails (i.e., succeeds) miserably (the docs do not warn not to compare environments): e1 = new.env(parent=emptyenv()) e2 = new.env(parent=emptyenv()) assign("foo", "bar", e1) compare(e1, e2) # oops? back to the original example, how come? vQ ---- for those curious, try the following in python: map(lambda func: func(), [lambda: i for i in range(5)]) map(lambda func: func(), (lambda: i for i in range(5)))
so just to show how scheme would do that: (map (lambda (function) (function)) (map (lambda (index) (lambda () index)) '(1 2 3 4 5))) ;; *this* is the scheme semantics vQ xxx wrote:> I agree, something is wrong here, especially since the R community > likes to say that R semantics are based on Scheme.... > > On Mon, Nov 17, 2008 at 4:28 PM, Wacek Kusnierczyk > <Waclaw.Marcin.Kusnierczyk at idi.ntnu.no> wrote: > >> the following is a trivialized version of some functional code i tried >> to use in r: >> >> (funcs = lapply(1:5, function(i) function() i)) >> # a list of no-parameter functions, each with its own closure environment, >> # each supposed to return the corresponding index when applied to no >> arguments >> >> sapply(funcs, function(func) func()) >> # supposed to return c(1,2,3,4,5) >> >> there is absolutely nothing unusual in this code, in the context of >> functional programming. >>
Wacek, I think when people say that R semantics are derived from Scheme, all they mean is that R supports lexical closures. But R has other features which are very un-Scheme-like, and when they interact with lexical closures, you get behavior you don't find in other functional languages. R passes function arguments using a "promise" mechanism somewhat similar to Algol 60's call-by-name, but caching the result when it is evaluated, what is sometimes called call-by-need. If the argument value is not needed, it keeps it as a "promise" (what Algol 60 called a "thunk"). Thus, we have: lapply(1:5,function(i) function( ) i )[[1]]() => 5 since the inner i is actually bound to a 'promise' which seems to point to the variable used by lapply for counting. Compare with the result of: lapply(1:5,function(i) { i<-i; function() i } )[[1]]() => 1 Scheme of course does not have the "promise" mechanism so this doesn't happen. I'm new to R, so I don't know if this is considered a bug or a feature. -s On Mon, Nov 17, 2008 at 4:28 PM, Wacek Kusnierczyk <Waclaw.Marcin.Kusnierczyk at idi.ntnu.no> wrote:> the following is a trivialized version of some functional code i tried > to use in r: > > (funcs = lapply(1:5, function(i) function() i)) > # a list of no-parameter functions, each with its own closure environment, > # each supposed to return the corresponding index when applied to no > arguments > > sapply(funcs, function(func) func()) > # supposed to return c(1,2,3,4,5) > > there is absolutely nothing unusual in this code, in the context of > functional programming. > the following is my best translation to python (modulo indexing, which > is inessential), where it does what i wanted: > > funcs = map(lambda i: lambda: i, range(5)) > map(lambda func: func(), funcs) > # [0,1,2,3,4] > > all these functions have distinct environments: > > (envs = sapply(funcs, function(func) environment(func))) > assign("i", 0, environment(envs[[1]])) > sapply(funcs, function(func) func()) > > interestingly, identical says they are all unequal, but compare disagrees. > > check = function(equal) for (i in 1:4) for (j in i:4+1) > print(equal(envs[[i]], envs[[j]])) > check(identical) > check(compare) > > compare seems to cast environments to character; for identical, the docs > give an example where environments are compared, but compare fails > (i.e., succeeds) miserably (the docs do not warn not to compare > environments): > > e1 = new.env(parent=emptyenv()) > e2 = new.env(parent=emptyenv()) > assign("foo", "bar", e1) > compare(e1, e2) > # oops? > > > back to the original example, how come? > > vQ > > > ---- > for those curious, try the following in python: > > map(lambda func: func(), [lambda: i for i in range(5)]) > map(lambda func: func(), (lambda: i for i in range(5))) > > ______________________________________________ > R-help at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code. >
On Mon, 17 Nov 2008, Wacek Kusnierczyk wrote:> the following is a trivialized version of some functional code i tried > to use in r: > > (funcs = lapply(1:5, function(i) function() i)) > # a list of no-parameter functions, each with its own closure environment, > # each supposed to return the corresponding index when applied to no > arguments > > sapply(funcs, function(func) func()) > # supposed to return c(1,2,3,4,5) > > there is absolutely nothing unusual in this code, in the context of > functional programming.What is unusual is R's lazy evaluation. Each function gets a promise to evaluate i, but the promise isn't forced until i is 5. Lazy evaluation is sometimes confusing, but it's the price R pays for not having macros. If you want to get around this (rather than just to construct an example), use force(). (funcs = lapply(1:5, function(i) {force(i);function() i})) -thomas Thomas Lumley Assoc. Professor, Biostatistics tlumley at u.washington.edu University of Washington, Seattle
You can use the 'local' function to make sure you create a value of 'i' that is defined when the function is defined:> funcs = lapply(1:5, function(i)local({i; function(){ i}})) > funcs[[3]]()[1] 3> funcs[[2]]()[1] 2 On Mon, Nov 17, 2008 at 4:28 PM, Wacek Kusnierczyk <Waclaw.Marcin.Kusnierczyk at idi.ntnu.no> wrote:> the following is a trivialized version of some functional code i tried > to use in r: > > (funcs = lapply(1:5, function(i) function() i)) > # a list of no-parameter functions, each with its own closure environment, > # each supposed to return the corresponding index when applied to no > arguments > > sapply(funcs, function(func) func()) > # supposed to return c(1,2,3,4,5) > > there is absolutely nothing unusual in this code, in the context of > functional programming. > the following is my best translation to python (modulo indexing, which > is inessential), where it does what i wanted: > > funcs = map(lambda i: lambda: i, range(5)) > map(lambda func: func(), funcs) > # [0,1,2,3,4] > > all these functions have distinct environments: > > (envs = sapply(funcs, function(func) environment(func))) > assign("i", 0, environment(envs[[1]])) > sapply(funcs, function(func) func()) > > interestingly, identical says they are all unequal, but compare disagrees. > > check = function(equal) for (i in 1:4) for (j in i:4+1) > print(equal(envs[[i]], envs[[j]])) > check(identical) > check(compare) > > compare seems to cast environments to character; for identical, the docs > give an example where environments are compared, but compare fails > (i.e., succeeds) miserably (the docs do not warn not to compare > environments): > > e1 = new.env(parent=emptyenv()) > e2 = new.env(parent=emptyenv()) > assign("foo", "bar", e1) > compare(e1, e2) > # oops? > > > back to the original example, how come? > > vQ > > > ---- > for those curious, try the following in python: > > map(lambda func: func(), [lambda: i for i in range(5)]) > map(lambda func: func(), (lambda: i for i in range(5))) > > ______________________________________________ > R-help at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code. >-- Jim Holtman Cincinnati, OH +1 513 646 9390 What is the problem that you are trying to solve?