Hendrik Boom <hendrik@???> writes:
> On Tue, May 24, 2016 at 08:40:37PM +0100, Rainer Weikusat wrote:
[same as below but worded differently]
>>
>> ... and what the Lisp 1.5 FUNCTION was about was to enable solving the
>> so-called 'upward funarg problem': Assuming a function is returned
>> (passed upward) when evaluating an expression and later activated in a
>> different context, what are free variables used by the returned function
>> supposed to refer to, the values they had at the time when the function
>> was defined or the values they had when the function is activated? Eg,
>> assuming this code,
>
> And by wrapping FUNCTION around every lambda-expression (which I did in
> those days) you oachieve lexical scoping.
By doing that, one achieves capturing the current, dynamic environment,
IOW, turning the returned function into a closure. But that's (as I
already wrote) something different from 'lexical scoping'. I don't have
access to anything where I could execute that but the following
(contrived) example ought to show the difference:
-----
(defun 1+x () (1+ x))
(defun one-more (v)
(let
((x v))
(lambda () (1+x))))
(setq five (one-more 4))
(setq x 15)
(funcall five)
------
In a purely dynamically scoped environment, this will return 16. In a
dynamically scoped environment with support for closures, it would
return 5 as the (1+x) would be executed in an environment where x was
dynamically bound to 5. If (let ...) would create a lexically scoped
binding, the return value would again be 16 because the effect of the
let wouldn't be visible to 1+x.
As far as I could gather from availabe literature[*], LISP 1.5 used an
a-list for binding symbols to values. This means the let executed as
part of the
(one-more 4)
would put a
(x . 4)
onto this a-list. A pointer to that would be saved in the 'closure
object'. The later
(setq x 15)
would create an a-list
(x . 15)
but it would then be changed to the stored value during execution of
five.
[*]
The LISP "alist" implementation is, as we noted earlier, an
example of the "deep access" approach". The "alist" contains
pointers to previous values of all bound variables together with
the variable names. The name of the variable appears on the left
side of a pair, and the value of the variable on the right side.
[ftp://publications.ai.mit.edu/ai-publications/pdf/AIM-199.pdf,
p. 5]
[...] when one transmits a functional argument f which is to be
evaluated in its binding environment, then one uses FUNCTION(f)
instead of QUOTE(f). [...] The result of FUNCTION will be a
structure which not only contains a reference to the function f
but also contains a pointer to the binding environment. Thus at
the FUNARG's activation time we will be able to use the pointer
to restore the environment to the proper place.
[...]
In the "alist implementation, this process is even easier,
because the save pointer is made the current "alist", and we are
done because the values of free variables will be obtained from
the "alist" which contains the binding environment.
[p. 7, 8]