It's simply a very bad idea that provides no use yet creates many bugs.
It's simply a very bad idea that provides no use yet creates many bugs.
Before the early aughts, closures were mostly really common in functional languages which tend towards immutable bindings (and immutability in general), and very closure-focused languages closured everything so didn’t hit that issue (e.g. you wouldn’t hit it in Smalltalk because your counter would be a parameter to a block, so closing over that was no issue).
It’s really in the 00s with the explosion of callbacks-pile-javascript (and more generally the functionalisation of imperative languages) that the problem became a serious concern: you loop over a thing, you start some sort of async operation (network request for instance), and you find out that despite the request being correct the entire thing goes wonky (then again things commonly went wonky which didn’t help).
It's a problem with references in general as this shows.
I also don't feel it's easier to implement at all.
One can either rewrite:
for $id:var in $exp:iter { $code:body }
to: { let $id:var;
while(True) {
let result = $exp:iter.next();
if(result.is_none()) break;
$id:Var <- result.extract();
$code:body
}
}
Or while(True) {
let result = $exp:iter.next();
if(result.is_none()) break;
let $id:var = result.extract();
$code:body
}
The latter implementation is as far as I see easier, not more complex. Obviously all the code to create scoping already exists in the compiler and for-loops over an iterator work with a syntactic rewrite to an infinite loop with a break.Most languages don't have references, and in those that do before the issue was understood, the explicitness made it a much smaller issue.
> I also don't feel it's easier to implement at all.
> One can either rewrite:
Now try lowering to bytecode or assembly instead of high-level pseudocode.
But Go and C++ do, where this issue arose with or without closures.
> Now try lowering to bytecode or assembly instead of high-level pseudocode.
It doesn't matter, because as I said, all that is already in the compiler.
It would be needlessly complex and error-prone for compilers to hardcode custom code generation for such abstractions; it's transformed to something else the compiler already understands at a far higher level. I know for a fact that in Rust, for-loops already desugar to a simple infinite loop construct with a break at the H.I.R. level and all further optimizations only happen from there.
The only explanation I see is that they really gave it no thought at all whatsoever and it wasn't a tradeoff but simply not thinking clearly.