Reputation: 495
I have read section 6.7 of LOL a few times now, and I still can't wrap my mind around the following.
Bindings that were previously closed to outside code are now wide open for us to tinker with, even if those bindings were compiled to something efficient and have long since had their accessor symbols forgotten.
If bound symbols are essentially compiled down to pointers in the environment of the closure, how can you pass a symbol to the already compiled function, and the function somehow is able to compare the symbol?
I've been messing with the pantest
example in clisp, and I can see that I'm able to change both acc
and this
inside pantest
. I can compile and disassemble pantest
, but all the symbols show up in the environment. If I had a lisp that compiled down to assembly, I might gain some more intuition, but the code is complicated enough that it will probably be too difficult to follow without explanation.
Upvotes: 1
Views: 275
Reputation: 286
(I will come back later and fill in some more information here later)
In short (and slightly oversimplified), the pandoric-let macro has added in some extra code to handle the case of trying to get or set each of the different variables it introduces. This extra code remembers the symbols of the variables after the code has been compiled, but since it's the only code which needs that information everything else gets compiled down to very efficient pointer operations.
The functions which generate this extra code are pandoriclet-get
and pandoriclet-set
, both of which are tricky to read because they return code which depends on the symbols sym
and val
which are provided in the pandoriclet
macro itself.
Upvotes: 1
Reputation: 11839
I'm not familar with Let Over Lambda.
The book Lisp in Small Pieces explains how lexical binding can compile down to very efficient variable references. Since all known references to the variables are in a limited scope, you could use an array to store the bindings and reference them by a numeric index, rather than by using the symbol to look up things or using a property of the symbol to get the value.
A symbol passed into a function is just a symbol, a kind of data. Comparing it to other things in the function isn't the same as accessing information about the lexical bindings in a particular scope.
There's a didactic Lisp pseudo-OO technique that shows how you can change function behavior by passing in a symbol to access and modify the lexical state, but it's choosing from a fixed set of known things to compare, not an arbitrary lookup of lexical info based on a symbol.
(defun make-incrementor (initial-value)
(let ((value initial-value))
(lambda (action)
(ecase action
(:value
value)
(:increment
(incf value))
(:reset
(setf value initial-value))))))
> (defvar *inc* (make-incrementor 10))
*INC*
> (funcall *inc* :increment)
11
> (funcall *inc* :increment)
12
> (funcall *inc* :increment)
13
> (funcall *inc* :reset)
10
This is manipulating the lexical binding of value
without externally accessing it. All changes are mediated through code in the same lexical location.
Upvotes: 2