
Derek Elkins wrote:
Lambda abstractions should close over bindings. Full stop.
Interesting. I agree with your analysis. I don't think I agree with your conclusion.
The first "surprising" behaviour is the correct one. The latter would be broken.
In my opinion, the reason this behaviour is "surprising" isn't mutability, but -implicit- mutability. Let's make bindings immutable, but add ML-style references to your example.
char ref c = ref(undefined); while(!eof(fp)) { c := getChar(fp); bind_event( ... print !c; ... ); }
compare this to
while(!eof(fp)) { char c = getChar(fp); bind_event( ... print c; ...); }
or
while(!eof(fp)) { char ref c = ref(getChar(fp)); bind_event( ... print !c; ...); }
Each of these examples makes it clearer what is going on.
Agreed. I think where I differ on you is how to map the semantics of a C-like language to explicit references. I would argue that the glyph "c" in a C-like language denotes the value of C, not the reference to it. C-like languages have, for the most part, value semantics, and call-by-value. The exception of course is what C-like languages called "lvalues", but lvalues are only really on the left of the = sign and a few other special positions. I think that's the exception and not the rule. I think the rule is that "c" denotes the value of c, and that's why I expect a closure to capture the value, not the reference. In C, of course, if you want to capture the reference you do it explicitly with "&c". Jules