3 ms·
Perhaps I am missing something/unable to interpret the article properly. I remember there was a day when it clicked in my head that the way the lexical scope/c
by ACow_Adonis 11y ago
Perhaps I am missing something/unable to interpret the article properly.
I remember there was a day when it clicked in my head that the way the lexical scope/compiler in common lisp works means that the variable names don't stick around at run-time, but are compiled away. So of course, using the lexical scope of (let) forms in lisp means that yes, it is quite impossible to have a naive variable symbol passed into a let form and looked up at run time as though it were still around.
But does that mean its not possible? I mean...doesn't Common Lisp at least have both lexical and dynamic scope? What if there was some kind of primitive function or form available that told lisp that you actually want to keep the names around and use that name to do run time environment/variable lookup instead. Then you could pass in such a quoted form and evaluate it and it would work.
Hang on a second...
Lets see if we can re-write that form from the article that "doesn't work" in lisp...in an easy and convenient fashion.
Here's his function definition:
(defun greater10 (value code) (if (> value 10) (eval code)))
And now lets set up a dynamic binding/evaluation using the less common progv form:
(progv '(msg) '("Hello")
(greater10 20 '(print msg)))
And the result:
"Hello"
- sedachv 11y agoI am not familiar with Rebol but from what I gathered from the "So...What Can't Be Done?" section of the article is that `use` is more like a code walker that does lexical binding before the given expression is expanded and evaluated (hence the need for `compose`). If I understand things correctly this is a lot like the `defmacro!` and sub-lexical scoping that Doug Hoyte writes about in _Let Over Lambda_. There the intention of code walking and rewriting is to prevent unwanted variable capture (walking over the code and replacing certain names with unique ones that have their own unique locations is basically implementing lexical scoping). So where `progv` might run into trouble is: (progv '(msg) '("Hello") (greater10 20 '(foobar msg))) Where foobar is something like: (defmacro foobar (x) `(let ((msg "abc")) ,x)) And a Common Lisp `use` might be something like: (defmacro use (var binding expression) (let ((var1 (gensym))) `(let ((,var1 ,binding)) ,(subst var1 var expression))))
- draegtun 11y agoYou should find this of interest because this is how USE is written in Rebol: >> source use use: make function! [[ "Defines words local to a block." vars [block! word!] "Local word(s) to the block" body [block!] "Block to evaluate" ][ apply make closure! reduce [to block! vars copy/deep body] [] ]]
- oalae5niMiel7qu 10y agoSBCL has EVAL-IN-FRAME, which, used in conjunction with FIND-CALLER-NAME-AND-FRAME, can be used to access variables by name at runtime. The variables may or may not actually be there depending on the OPTIMIZE settings. SBCL removes unreferenced variables. Here's how to implement LEXICAL-EVAL, which evaluates its argument in the current lexical scope instead of in the toplevel environment: (defpackage :lexical-eval (:use :common-lisp :sb-kernel :sb-di)) (in-package :lexical-eval) (defmacro second-value (form) `(multiple-value-bind (a b) ,form (declare (ignore a)) b)) (defun lexical-eval (form) (eval-in-frame (second-value (find-caller-name-and-frame)) form)) Then you can do this: (let ((x 2)) ;; Prevent X from getting optimized out: (setf *some-global-var* x) (lexical-eval 'x)) In CLISP, there is EVAL-ENV and (THE-ENVIRONMENT), which can be used to accomplish the same thing.