3 ms·
They're talking about capture by value or reference. If you use capture by value, created closures will use the value of captured variables at the time the clos
by ImprobableTruth 5y ago
They're talking about capture by value or reference. If you use capture by value, created closures will use the value of captured variables at the time the closure is generated.
e.g. in C++, where one has to make capture by reference explicit
int i = 0;
auto f = [x = i, &y = i] { //x captures i by value, y by reference
std::cout << "Value:" << x << ", Reference:" << y;
};
i++;
f();
will print out "Value: 0, Reference: 1".
- jhgb 5y agoI'm not sure you can call this "a closure". That term has a rather specific meaning related to lexical scoping which simply doesn't deal with copies or references or anything of the kind. There's no "capture by value or reference" since it's not a copy of a value, or even a reference to it -- it's one and the same variable binding inside and outside of the closure (whereas a reference would be another variable name referring to the same value, or at least a differently scoped variable of the same name referring to the same value, but that's not lexical scoping anymore). To put it bluntly, C++ is faking closures, since without subjecting environments to GC or refcounting, it can't delete the shared environments at just the right time. They just jumped on the bandwagon of closures being fashionable nowadays and tried to emulate closures as closely as possible by constructing artificial automatically generated objects in the background using some syntactic sugar without breaking the existing language.
- ImprobableTruth 5y agoI don't really understand your objection. How is >at least a differently scoped variable of the same name referring to the same value, but that's not lexical scoping anymore not lexical scoping? That's literally just how closures work under the hood. And that's exactly why closures have to deal with references (or copies) - every implementation of them is a function pointer and a record for the lexical environment, the latter of which requires that at closure creation time the relevant bindings are captured, either by creating a reference or making a copy. I also don't understand why C++'s (or Rust's) closures would be 'fake'. If you capture by reference, they work exactly like the closures of other languages. Not having a GC just means you have to be wary of lifetimes. If you use a reference counting pointer (/some other GC'd pointer) you literally have the same closures as other languages.
- jhgb 5y ago> How is ... not lexical scoping? That's literally just how closures work under the hood. In the presence of references in the language, these two cases may be observationally indistinguishable (maybe, I'm not a C++ guru?) but they're still different language features. Lexical scoping does not require taking a reference. > And that's exactly why closures have to deal with references (or copies) No, they don't. They deal with bindings. References are either an explicit thing you create (as in, int &b=a; or such), or a parameter passing mechanism...but a free variable is neither (it's not a parameter to the closure in any case), and it exists even in languages with no notion of references whatsoever, such as for example Scheme. > I also don't understand why C++'s (or Rust's) closures would be 'fake'. If you capture by reference, they work exactly like the closures of other languages. Not having a GC just means you have to be wary of lifetimes. Yeah, and that's at least one of the things that make it fake. Why would you have to be wary about lifetimes? Closures in languages that support them Just Work(TM).
- CornCobs 5y agoWhat do you mean by "languages with no notion of references whatever"? At least in Scheme I think references are a pretty immediately obvious feature. It's not like every assignment does a deep copy, nor does free variable capture in closures. The moment you do stuff with set-car! and friends you are very explicitly working with references
- jhgb 5y agoScheme doesn't have references. > The moment you do stuff with set-car! and friends you are very explicitly working with references No, you're not. At least not in the sense that references are references in C++ or in the established CS term call-by-reference. Likewise, you probably wouldn't say that C has references because you can "do stuff" with pair->car=foo; in C.
- hackinghaskell 5y agoYes, you are. Notwithstanding the fact that C pointers are syntactically not really references, at least in my opinion, the way to spell an example for what they are referring to in C would be "pair = foo". You aren't modifying *pair, but the reference object stored in your own private stack frame. As long as mutability is out of the picture and the aptly named notion of referential transparency is preserved, there is no way to discern whether any two symbols reference the same value. The moment you flip the proverbial page to chapter 3 of SICP and start using setter-functions!, you lose this. You should very much become aware of the fact you are working with references whenever you mutate things, because the mental model of lists as values falls apart when you write, for example: (define a (list 1 2 3)) (define b (cdr a)) (set-car! b 'ta-da!) (display a) And then get left to perplexedly stare at the REPL's output. It is true that references are not, in some sense, a language-level/opt-in feature of Scheme, as in that you cannot arbitrarily create a reference to an integer, but cons cells very much are represented by references to them only, in much the same way as class types in Java are.