I would love some of the proposals surrounding errors to move forward, of course. But I'm OK with the current state, and will be happier with 1.18.
for bar := range list {
}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".>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.
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).
> 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.
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.
This strikes me as an unusual definition. I'd say that references are simply a pointer that is treated like the underlying value i.e. they're 'boxed' values. This is a notion that is essentially present in all languages. e.g. Java (or Scheme) which don't have some explicit reference feature, still have 'unboxed' values (e.g. integers) and 'boxed' values (e.g. lists).
>They deal with bindings.
Sure, to create a closure, you need to capture the relevant bindings (the lexical environment). But how does on deal with bindings? Well, a binding associates a variable to a value. Now, when you capture a binding, you can either point to the original binding (capture by reference) or copy the value (capture by value).
>Why would you have to be wary about lifetimes?
I mean, that's just how these languages are, right? Even if you do something incredibly mundane, like returning a value or calling a function with a parameter, you constantly have to be aware of lifetimes. That's why to me it seems like a completely orthogonal issue to closures.
C++ lambdas do the same thing if you pass a pointer.
Python would do the same thing if you had a mutable non-threadsafe objects passed to threads. Eg if you iterate over a list of dicts, and modify the dicts, you'll get the same effect.
So then the question becomes, why does v get re-used, instead of assigning a new pointer each time? Dunno, probably performance. I could see the latter generating a lot of garbage, which can be totally avoided if you don't pass mutable references to goroutines.
What go really lacks IMHO is some kind of const modifier for these kind of references. Maybe it does, all I remember are const scalars.
You'll have the same problem with a simple integer (for example, as in https://go.dev/play/p/uqvRoO3ynZt). Nothing to do with v being a reference to something.
>>> funcs = []
>>> for i in range(10):
... def f():
... print(i)
... funcs.append(f)
...
>>> [f() for f in funcs]
9
9
9
9
9
9
9
9
9
9
[None, None, None, None, None, None, None, None, None, None]For me, Go is essentially pointless without generics and better error handling (I love that there are no exceptions, but since 99% of the code just return the error anyway, that needs to be the default path), but even with it, it is not much more than C with garbage collection, a few nice features and better syntax in some cases.
So even if it got these problems solved well, I still don't really think I would be using it.
There are only 2 kinds of languages: Those people complain about, and those nobody cares about.
If you seriously think that's gonna happen, you haven't been to Go mockery stations on the interweb.