4 ms·
> one really great feature of Wirth's Pascal is nested functions with access to outer scopes, which D wholeheartedly embraces Can you give an example? What doe
by dist1ll 3y ago
> one really great feature of Wirth's Pascal is nested functions with access to outer scopes, which D wholeheartedly embraces
Can you give an example? What does the function access in the outer scope? Is it like an environment-capturing closure?
- WalterBright 3y agoIt can access the variables in an outer scope: int moon(int s) { int sum(int x) { return s + x; } return sum(3); } An extra parameter is passed to the nested function, called a "static link", which is a pointer to the stack frame of the statically enclosing function. This is in addition to the usual "dynamic link" which is a pointer to the stack frame that called the nested function. Nested functions can also be nested, and can access variables in enclosing functions by walking the static links back. The neato thing about this is it makes stack frames work exactly like members of a struct. In fact, a pointer to a nested function is analogous to (and binary interchangeable with) a pointer to a stack object.
- loup-vaillant 3y agoMy first (and only) serious compiler had such nested functions, though without stack frames. Instead my VM had two stacks: one for function arguments and locals, and the other for return addresses, same as Forth. I had no stack frame at all. Instead, the compiler kept track of the stack offset of every accessible local (relative to the top of stack). That way the implementation of my nested function was kind of trivial: there were no difference between true locals like `x` and locals from an outer scope like `s`, except of course for the possibility of shadowing. One reason this was not special is that internally, it was nested scopes all the way down: there was one scope per local variable, so merely declaring two variables already means dealing with nested scopes. Once that was out of the way adding nesting functions was really easy (I believe it added less than 20 lines to my compiler). Nowadays I think I would use a single stack instead, but if my language is simple enough I’ll probably skip the frame pointer and just keep track of offsets like I did this first time.
- kragen 3y agoif your outer function f called a function 'quicksort' which called a function 'partition' which called your nested function 'compare', how did 'compare' get access to the variables of f from its statically enclosing scope? how did it know how many locals 'quicksort' and 'partition' had pushed onto the operand stack?
- loup-vaillant 3y agoErr… I think recursive inner functions would just blow up… oops. I guess it’s a good thing I didn’t advertise inner functions and only used them in the standard library…
- kragen 3y agowell, that's the problem the static link solves; it's not such a difficult thing to implement if you let procedure values (function pointers) be two words instead of one. gcc emits trampoline code onto the stack at runtime to support this without breaking abi compatibility, so the pointer to the nested function actually points to the trampoline, which just supplies the static link and calls the real nested function
- criddell 3y agoDo you know if that’s how C++ lambda functions work? auto sum = [=](int x) { return s + x; }
- ReleaseCandidat 3y agoAlmost, the nested function goes out of scope too if the outer function finishes.
- WalterBright 3y agoTrue. D handles that complication allocating such scopes on the GC heap, if the nested function survives the termination of the other function.