Robert said:
Wow! All these years when I occasionally heard of a "thunk" I never
was told, until now, what it really meant. Thanks for the info!!
Followup question #1: I assume these are lexical closures in the
environment of the point of the call, right?
Yes. (Actually, subprogram calls are first described as working like
macro expansions, but then the specification adds that there must be
magic fixups so that variable names are resolved in point-of-call
context anyway.)
At this point in the history of computing, the conceptual distinction
between subprograms and macros was not at all clear. It was quite
possible to have "macros" that generated an out-of-line subprogram once
and then generated calling code the first time and every time thereafter
(it was a way of life on systems without linkage editors or linking
loaders), and it was also quite possible to refer to in-line macro
expansions as "subprograms". I suspect that the triumph of FORTRAN may
have had something to do with cleaning up the terminological mess by the
mid-60s.
Into the 60s, indeed, there were still machines being made that had no
instruction comparable to the mainframe BASx/BALx family, or to Intel's
CALL. You had to do a subprogram call by first overwriting the last
instruction of what you were calling with a branch instruction that
would return back to you.
Followup question #2: For simple arithmetic expressions, I can
possibly understand how the UPDATE closure might be implemeted
(expressed in Lisp to make the intent clear):
Call form: MyFunction(X+2);
GET closure: (+ closedX 2)
UPDATE closure: (lambda (newval) (setf closedX (- newval 2))
Thus from inside MyFunction where formal parameter Arg1 is bound
to actual parameter X+2, after doing Arg1 := 7; X will have the
value 5 so that calling Arg1 will return 7 as expected, right?
But if the actual argument is something complicated, especially if
it makes a nested function call, how can that possibly be
implemented?
It was forbidden. In the formal definition of ALGOL 60, there was no
such thing as an external subprogram (except for non-ALGOL code, which
was treated as magic), so the whole program was supposed to be one
compile. Therefore, a theoretical compiler could verify that any
parameter used as an LHS was always matched with an argument that was a
variable. (However, a "thunk" was still required to evaluate any array
index and, possibly, to convert between REAL and INTEGER variables.)
Many actual compilers /did/ support separate complication as a language
extension -- I suppose they had to use run-time checking.