Falk said:
Well, I tried to google what Ada "downward closures" are but since I
don't know the language, I'm not sure I properly understood how they
work (shame on me!).
I believe downward closures are not at all Ada-specific.
In Ada, you pass a function f as a parameter to another
function g(..., f) *even* when f is declared at a place more
deeply nested than the declaration of g.
So f has access to objects up the nesting level, i.e. to
its environment, g has not.
For example, the Ada container library provides procedures
"iterate", consuming a container and a procedure pointer.
In abbreviated peudo-notation:
container library:
procedure iterate(a container X, a procedure P);
-- invoke P for every element in X
your program:
procedure work (a container X) {
... local declarations
procedure f (a cursor K) {
-- local procedure with access to X's element a K,
-- and to the local declarations
...
}
iterate(a container, f);
}
When iterate executes, both the local declarations and f still
exist, they are accessible. But iterate is declared somewhere
else, f is more nested.
The accessibility rules imply that iterate can only invoke f, or
pass it on to other procedures, but f cannot for example be
assigned to a global variable because when work has executed,
f becomes inaccessible.
Here is a small and simple Ada 2005 example that I think comes
fairly close to the (here named) lambda expression you have given.
procedure find_gt_666 is
vec: Vector; -- of integers
here: Cursor := No_Element; -- end()
procedure first_gt_666(x: Cursor) is
begin
if not Has_Element(here) and then Element(x) > 666 then
here := x;
end if;
end first_gt_666;
begin
... populate vec
iterate(vec, first_gt_666'access);
... use Element(here)
end find_gt_666;
Of course, it would be nicer, easier and more flexible if you could write
something like (syntax totally hypothetical!):
std::vector<int>::iterator it = std::find_if(vec.begin(), vec.end(),
lambda{x; x>666} );
Eiffel agents might come even closer to this as they can be defined
in place.
Georg