And to be the devils advocate: Why does a closure have to be refcounted? It could be freed by the user.
Because it can be copied around. It can be passed to function A and function B, maybe function A stores it in variable X and function B stores it in class instance Y. Some time later class instance Y is freed, but X is still active. So there must be some form of reference counting. In Delphi they simply used what was already there and supports methods: reference counted interfaces.
I am not against the ref counted part. [1]
However, it does not implicitly follow from the concept of closure.
Method pointers can be passed around too. They include a pointer to an instance, which may be dangling if the instance was freed.
Of course method pointers do not need to be freed, because they are a record type, and do not by default allocate heap.
But instances of classes do alloc heap, and we free them ourself all the time. So that would work for closures, if desired (I am fine with it not being desired)
[1] => I understand that if smart pointers ever come, then ref-counting becomes available for more than just closures.
And in that case, especially (but not limited to) if closures would not be anonymous, but declared upfront, the declaration could include if recounting should apply.
(yes probably over engineered, but just as a point)
In the end certain concepts will end up tightly bound together as if they were one. There is no issue with that.
At the moment I can not help that many of those talking about it, are doing so with ignorance to those concepts.
It was just mentioned that an anonymous function (because it will be a closure) can not be used as a substitute for a normal callback (such as for TStringList.FOnCompareText)
Now FOnCompareText is a method pointer. Lets say we needed a function pointer: type TOnFoo = function: boolean;
OnFoo := function: boolean begin .... end;
Currently (the way it is currently thought of afaik) that would create a closure, and would not work.
What if there were
OnFooFunc := function: boolean begin .... end;
OnFooMeth := function of object: boolean begin .... end;
OnFooClosure := nested function: boolean begin .... end;
Creating:
- A plain unnamed function. This does *not* have access to the outer functions variables. It is not a nested function.
- A plain unnamed method. Only work, if the outer routine is a method. This does *not* have access to the outer functions variables, except it gets a copy of "self". It is not nested.
- A nested function, which becomes a closure.
Mind that a closure could also/optional be declared with a name, like a normal nested function. It would then be instantiated (i.e. outer vars would be captured) by the "@" operator, or with a dedicated operator, keyword, or similar.
I have not quite followed, if reference to normal nested function work and how, and if they differ from closures? If so, then the last item would need splitting.
With those distinction for anonymous functions, it would be possible to write code like this
ATStringList.OnCompareText := function of object(const s1, s2: string): PtrInt begin ... end;
And that is without changing the current signature of TStringList.FOnCompareText
Personally, I think declaring a closure up-front (with a name, like a nested function), is way more pascal-ish. But I do see that there are cases, where having them inline can be a blessing. Still, at least, have the syntax make it very clear that indeed they are a closure.
And, yes, I know, there is the fact that we also need a version of what Delphi does....