On Sat, Jun 5, 2010 at 08:05, Vince O'Sullivan <[email protected]> wrote:
> On Jun 2, 2:51 am, Reinier Zwitserloot <[email protected]> wrote:
>> NB: The 'final limitation' exists because of confusion. For example,
>> what does this print:
>>
>> Runnable[] r = new Runnable[3];
>> for (int i = 0; i < 3; i++) r[i] = #() {System.out.print(i);};
>> for (int i = 0; i < 3; i++) r[i].run();
>>
>> Looks like it ought to print "123", right? Nope. It would print "333".
>> Which is confusing.
>
> OK, I'm confused. Explain, please.
Actually, I think Reiner's example is subtly misleading since each
iteration of the for loop conceptually gets a fresh variable 'i',
else using the final modifier would not be possible.
The example, using current Java:
Runnable[] r = new Runnable[3];
for (final int i = 0 ; i < 3; i++)
r[i] = new Runnable() {
public void run() { System.out.println(i); }
};
for (int i = 0; i < 3; i++)
r.run();
Prints 1 2 3
I suspect that if Java didn't require final i, the results would still
be the same, because the implementation would still be forced to copy
values into compiler-generated fields of the inner Runnable() class on
allocation. There's no way to store a reference to a variable in Java.
Having the compiler silently promote i to a mutable object on the heap
just so everyone can share a copy would be the only way to get the
behavior Reiner suggests and that just seems baroque.
***
There seems to be a lot of confusion in this discussion. There 's an
important distinction that we're glossing over, which causes us to
talk past each other. What *exactly* is the closure capturing? The
*variable* or the variable's *value*? This distinction is often not
clear to users of languages that pass everything by value, like Java.
A few examples:
# python
def demo():
x = 0
def foo():
x = 1
foo()
print x # prints 0
The inner function foo() captures read-only access to the outer variable x,
which leads to some oddities. x=1 actually creates a new 'x' local to
foo, hiding the outer x. reading from x in foo before assigning to it
will get us the outer x. Trying this x = x + 1 leads to a runtime
error because the x must necessarily be interpreted as local to foo in
that expression, but that means we can't read a value from it (the
second x in the expression) since it hasn't been bound (assigned to)
yet.
Python is capturing read-only access to the outer *variable* itself.
How can I tell it's not simply copying the value?
def example():
x = 0
def foo(): print x
foo(); x = 1; foo(); # output: 0 1
// java
void demo() {
final int x = 0;
new Runnable() {
public void run() {
x = 1; // COMPILE ERROR
}
}.run();
System.out.println(x);
}
Java protects us from the sort of confusion that Python can produce by
requiring outer variables be marked as 'final'. That way, there is no
expectation that they can be mutated from within the inner function
(the Runnable). Yes, the 'final' requirement is a little pedantic, but
at least
it's clear what the expected behavior is.
They could just as easily have defined that outer variables get copied
to compiler generated instance fields upon inner class creation (which
is what happens) and that left them mutable.
In any event, Java is capturing a *copies* of the *values* of outer
variables in its closure-like inner classes.
(* Oberon: sorry about the shouting *)
PROCEDURE Demo();
VAR x: INTEGER;
PROCEDURE Foo();
BEGIN
x := 1;
END Foo;
BEGIN
x := 0;
Foo;
Out.WriteInt(x); (* prints 1 *)
END Demo;
Foo has read/write access to the actual *variable* x, which is why
this example prints 1. The trade-off in this case is that the inner
procedure Foo can not outlive the call to Demo that created it. Oberon
doesn't have true Closures. The language enforces this by only
allowing top-level procedures to be assigned to variables are returned
from Functions.
;; clojure
(defn demo []
(let [x (atom 0)] ;; only references are mutable in clojure
(letfn [(foo [] (swap! x (constantly 1)))]
(foo)
(print @x)))) ;; prints 1
In Clojure the example almost doesn't make sense. Clojure variables
are all effectively final. i.e. They're not "variable", they're just
names for *values*. But it turns out, that some of these values can be
Clojure reference types, which allow mutability with defined
concurrency semantics.
Like Java, Clojure's closures capture the *values* of outer bindings
when they are created.
;; scheme
(define (demo)
(let ((x 0)
(foo (lambda () (set! x 1))))
(foo)
(print x))) ;; prints 1
Unlike Clojure, Scheme is permissive about mutability. Like Oberon,
foo captures the *variable* x. Unlike Oberon, this binding continues
to exist even after the call to demo completes. That is, foo has a
lifetime independent of demo.
As far as I am concerned, all of the presented languages (apart from
Oberon) can be said to have closures. Where they differ is in how
exactly they capture the surrounding environment. Do they capture
*values* or *variables*? Are they funky like Python?
// Ben
--
You received this message because you are subscribed to the Google Groups "The
Java Posse" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/javaposse?hl=en.