The implementation of closures is completely unnecessary for any of the features that exist in functional languages or in any programming languages.
The avoidance of closures becomes possible when the programming language includes the restriction that inside a function body the only external variables (i.e. which are neither parameters nor local variables) that are visible are those that are allocated statically, either as process-local or as thread-local.
This restriction is similar with the restrictions from C or from traditional C++, but it is less constraining, because nested functions are allowed, they just cannot see the stack-allocated variables from enclosing blocks, unless they are passed as parameters.
Any useful thing that can be implemented with stack-allocated variables captured inside a closure can also be done with statically-allocated variables, but in a much more efficient way and in a way that is less prone to subtle bugs. Other applications of closures in LISPy languages are just workarounds for the impossibility of defining record or structure types. In any language with user-defined data types such closure applications are not needed.
The only significant difference between a language with closures and one without closures, from the programmer POV, is that in a language without closures for each variable that is accessed inside a function body without being passed as a parameter, the programmer must add an allocation-class specifier, such as "static" or "thread_local".
This is a good thing in my opinion, because sometimes bugs are caused by unintentionally accessing external variables, which are automatically captured in closures.
A "purely" functional program must not access inside function bodies any variables that are not passed as parameters. Avoiding to write explicit "static" annotations, in order to make closures masquerade as functions, does not change the fact that such functions are no longer pure functions, but automata, which is good, whenever this is necessary.
When there are no closures, any function pointer can be just a simple pointer and any higher-order functions can have a faster implementation, which is better for any functional language. Closures add a completely unnecessary overhead.
Also, C++ doesn't suffer from the problem of accidentally capturing variables, since it requires you to explicitly state which variables from the external env you want to reference (with the [a, b] syntax at the start of a lambda declaration; a lambda starting with [] fits your desired constraints).
The members of the current instance are not really an extra type of variable.
They are the members of one hidden function parameter. While they are distinguished in the source code, in the translated machine code there is no difference between them and the explicit function parameters.
I agree that this is a good feature of C++, that it avoids the bugs caused by accidental captures by giving control to the programmer on what may be captured.
Nevertheless, perhaps it would have been possible to have a simpler implementation of the C++ lambda-declared functions than with the current function objects, by simply disallowing the capture of stack-allocated local variables.
Even in Java, this was there as part of Java 1.0, in the form of instance-scoped inner objects, in a form like this:
for(int i=0; i < arr.length(); i++) {
final int copy = i;
listeners = new ActionListener(){
public void actionPerformed(ActionEvent e) {
System.out.println("pressed button " + copy);
}
}
}For examples like this, if it is not desired to use a single function with one extra parameter, then multiple functions that differ in using some different constants or different private static variables can be generated by some template mechanism, without using closures.
The problem with closures is that they introduce an overhead in any function that is passed as a parameter or used for some other reason through a pointer, by adding an extra indirection, even if in most cases this is not needed, instead of adding overhead only in the few cases when capturing local variables is really useful (unless something like whole program optimization is used).
PHP has the “use” keyword, which captures variables explicitly. You can’t capture anything in the outer frame otherwise.
I wonder now if this has any performance implications.
In Rust, every captured variable is statically checked. The compiler knows that you capture a mutable reference for example. This often means that you need to express additional constraints in order to satisfy ownership semantics.
Now again, I wonder what the implications are on the generated code.
``` The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."
Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.
On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened. ```