proc f = (real x) proc (real) real: (real y) real: x + y;
proc (real) real g := f (3);
As by the time g is created, x of f has disappeared from the stack. proc f = (real x) proc (real) real: (real y) real: x + y;
proc (real) real g := f (3);
As by the time g is created, x of f has disappeared from the stack.For example if proc A contains a variable X and declares a subroutine B within it, then B can access X, even if you take a pointer to B and call it from elsewhere (effectively that pointer to B also carries info about which instance of A it came from ....
These days we've largely forgotten about this idea of how variables should be accessed and inherited between subroutines - hardware designers used to design support for these language features (display registers in Burroughs mainframes) - to be fair as a feature it honestly doesn't add a lot of practical functionality considering what it costs
"If the nesting level is 0, the processor pushes the frame pointer from the BP/EBP/RBP register onto the stack, copies the current stack pointer from the SP/ESP/RSP register into the BP/EBP/RBP register, and loads the SP/ESP/RSP register with the current stack-pointer value minus the value in the size operand. For nesting levels of 1 or greater, the processor pushes additional frame pointers on the stack before adjusting the stack pointer. These additional frame pointers provide the called procedure with access points to other nested frames on the stack. See “Procedure Calls for Block-Structured Languages” in Chapter 6 of the Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1, for more information about the actions of the ENTER instruction."
(Although, curiously, Free Pascal - which also implements this feature - doesn't use ENTER in assembly, last I checked.)
(define (make-counter)
(let ((n 0)) (lambda () (set! n (+ n 1)) n)))
is guaranteed to work in any context where make-counter is called. Same in Go; though the syntax is a bit unwieldy due to having to specify types: func makeCounter() (func () int)) {
n := 0
return func () int { n++; return n }
}
Google's Go compiler is smart enough to allocate only the captured variable (n in this case) for such returned functions on the heap. There should be no failures.IIRC some algol-68 compiler did remove the restriction and allowed such full closures.
Algol60 and 68 were created at a time when people were still trying to figure out how computer languages ought to work (in a sense we still are) and it's likely the idea that procedure local storage should (or could) out live the procedure being called wasn't an obvious choice (remember that algol68 Nests are not just locals of a particular subroutine but also locals of every subroutine in its declaration [not calling] path as well as globals at level 0)
Algol68 (and Algol60) gave us lots of things we take for granted today but there are also features that have fallen out of fashion
(I'm old enough to have seen language features like objects and functional programming come and go, several times ... it's all fashion .... and I'm cynical enough to know that there's no correct way to do any of this stuff)