简体   繁体   中英

R implemenation of Closure differs from other functional languages, which makes it behave like imperative language such as C#?

R is a functional language, but it behaves like C# instead of F# in the following case.

in R

f <- function() x
x <- 15
f() // prints 15
x <- 20 // shadowing
f() // prints 20

in C#

int j = 15;
Action<string> funs = x => Console.WriteLine(j);
funs("foo"); // prints 15
j = 20; // mutate
funs("foo"); // prints 20

in F#

let x = 15
let f y = x
f() // prints 15
let x = 20 // shadowing, to mimic R (purely functional)
f() // prints 15, not 20

In R, the example violates my understanding of " closure closes variables, either by value or by reference ". In C#, closure closes variable by reference. In F#, it doesn't matter as variables are immutable. But in R, it seems closure only closes the symbol name, (certain not by value as it does change; not by reference, since variables are immutable, it is not mutated but shadowed).

So, are there 3 ways that closure closes variables, by value, by reference, and by name ?

Is there any other functional language behaves like R in this case?

Here, Dynamic Lookup as defined in hadley's book seems to explain it a bit, but I couldn't find any other resources that have the term "dynamic lookup".

I'm not sure I completely understand your question, but perhaps these examples help a bit to illustrate how a value might be scoped (but these probably aren't very good examples!) but I strongly suggest you read the evaluation of expressions section of the R Language Definition (in fact I should probably go and read it again too):

# Define default value for x in function definition
f <- function( x = 15 ) x
f()
#[1] 15

# Assign value to x in .GlobalEnv
x <- 20

# But x is found in the environment of the function call because we already defined it there, so value we assigned inside the function is returned
f()
#[1] 15

# Now we pass an object, x to the function which causes it to search up the calling stack for an 'object' called x
f(x)
#[1] 20

# In this example we use 'get' to search for the object. In the help page it states:
# The default is to search from the current environment of the call to 'get'
f <- function() get( "x" )
f()
[1] 20

# But we can specify an environment to search in
# In this case we specify an environment where x was not defined
# and it is not found
f<- function() { pos <- sys.nframe()+1 ; get("x" ,  envir = as.environment(pos) ) }
f()
#Error in get("x", envir = as.environment(2)) : object 'x' not found

Coming from Scheme, I don't see the confusion.

Eg, using chibi-scheme (R7RS):

$ chibi-scheme
> (define f (lambda () x))
WARNING: reference to undefined variable: x
> (define x 15)
> (f)
15
> (define x 20)
> (f)
20

Should be the same for any compliant Scheme version (works in Guile and MIT/GNU Scheme, at least). REPLs may differ from compilers, but even if you wrote the above and compiled it, it would use a symbol table and resolve the reference after parsing the file. It would still work.

f has a reference to the value at the memory location designated by the variable x . When you do f <- 15 or f <- 20 you update the value and not the reference or location.

I may be wrong (or using technical terms incorrectly), but I still find this to be non-surprising.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM