1

In below situation, I want to get same results for a and (car b) like (10 10). But just symbol A is shown as a result of (car b). Can we get 10 with like (something (car b)) code?

(let* ((a 10)
       (b '(a)))
  (list a (car b))) ; (10 A)
  ;; Hoping to get (10 10) with (list a (something (car b)))

I also tried below ones but couldn't get the answer.

(let* ((a 10)
       (b '(a))
       (c `(,a)))
  (list a ; 10
        'a ; A
        (car b) ; A
        (eq (car b) 'a) ; T
        (car c) ; 10
        ;; (eval 'a) ; Error - The variable A is unbound.
        ;; (symbol-value (car b)) ; Error - The variable A is unbound.
        ))

My motivation is to make symbols and params lists with let like below. But the part of foo is not working as I expected now. I believe above question will solve (or help) this also. (This part is not question)

(defmacro my-let (binds &body body)
  `(let ((symbols (mapcar #'car ',binds))
         (params (mapcar #'(lambda (x) (eval (cadr x))) ',binds))) ; I may need to modify here
     (let ,binds
       ,@body)))

(defun foo (bar)
  (my-let ((b bar))
    (list symbols params)))

(foo "baz") ; Error - The variable BAR is unbound.
LMB
  • 13
  • 2
  • Does this answer your question? [When to use ' (or quote) in Lisp?](https://stackoverflow.com/questions/134887/when-to-use-or-quote-in-lisp) – Martin Půda Jan 31 '23 at 11:55
  • As for the macro, I'm not sure whether something like this is even possible- `eval` or `symbol-value` return the current dynamic value of a symbol, but your bindings are lexical. – Martin Půda Jan 31 '23 at 12:43
  • Thank you for your quick reply @MartinPůda. But I'm afraid the link is not answering to my question. I understood the article said "`quote` is to protect from eval. `eval` is to unquote". But my question is related with scope as you mentioned, probably. I may be interested with getting lexical value and will investigate them more. Thank you again. – LMB Jan 31 '23 at 13:33
  • I might get answer thanks to @MartinPůda kind comment (I didn't think about scope too much). [This article](https://stackoverflow.com/questions/17312607/eval-and-lexical-variables) suggested to do like `(let* ((a 10) (b '(a))) (declare (special a)) (list a (eval (car b))))` and this satisfies my question. Still I'm not sure this is formal way to do but it works anyway. – LMB Jan 31 '23 at 13:40

2 Answers2

1

[I wrote this mostly before coredump's answer was posted and then forgot about it. I think that answer is probably the one you want but perhaps this is worth reading as well.]

You can't retrieve the values of lexically-bound variables from their names. However, you can (as I think you are trying to do) write macros which bind variables and then remember the names of the bindings. Here are two, both of which introduce a local function called valof which lets you get at the value of a binding by name. Neither are completely correct: see the end for why.

For both of these macros the trick is to maintain a secret association list between names and values.

(defmacro let/names (bindings &body forms)
  (let ((<map> (make-symbol "MAP")))
    `(let ,bindings
       (let ((,<map> (list ,@(mapcar (lambda (b)
                                       (etypecase b
                                         (symbol
                                          `(cons ',b ,b))
                                         (cons
                                          `(cons ',(car b) ,(car b)))))
                                     bindings))))
         (flet ((valof (s)
                  (let ((m (assoc s ,<map>)))
                    (if m
                        (values (cdr m) t)
                      (values nil nil)))))
           ,@forms)))))

Here is what the expansion of this looks like (here *print-circle* is true so you can see that the gensymed name is used):

(let/names ((a 1))
  (valof 'a))
 -> (let ((a 1))
      (let ((#1=#:map (list (cons 'a a))))
        (flet ((valof (s)
                 (let ((m (assoc s #1#)))
                   (if m (values (cdr m) t) (values nil nil)))))
          (valof 'a))))

Now, for instance:

> (let/names ((a 1))
    (valof 'a))
1
t

> (let/names ((a 1))
    (valof 'b))
nil
nil

But this macro is not actually really correct:

> (let/names ((a 1))
    (setf a 2)
    (valof 'a))
1
t

To deal with this problem the trick is to build a bunch of little functions which access the values of bindings. While doing that we can also allow assignment: mutation of the bindings.

(defmacro let/names (bindings &body forms)
  (let ((<map> (make-symbol "MAP")))
    `(let ,bindings
       (let ((,<map> (list ,@(mapcar (lambda (b)
                                       (etypecase b
                                         (symbol
                                          `(cons ',b
                                                 (lambda (&optional (v nil vp))
                                                   (if vp
                                                       (setf ,b v)
                                                     ,b))))
                                         (cons
                                          `(cons ',(car b)
                                                 (lambda (&optional (v nil vp))
                                                   (if vp
                                                       (setf ,(car b) v)
                                                     ,(car b)))))))
                                     bindings))))
         (flet ((valof (s)
                  (let ((m (assoc s ,<map>)))
                    (if m
                        (values (funcall (cdr m)) t)
                      (values nil nil))))
                ((setf valof) (n s)
                  (let ((m (assoc s ,<map>)))
                    (unless m
                      (error "~S unbound" s))
                    (funcall (cdr m) n))))
           ,@forms)))))

Here, again, is what the expansion looks like (much more complicated now):

(let/names ((a 1))
  (valof 'a))
 -> (let ((a 1))
      (let ((#1=#:map
             (list (cons 'a
                         (lambda (&optional (v nil vp))
                           (if vp (setf a v) a))))))
        (flet ((valof (s)
                 (let ((m (assoc s #1#)))
                   (if m (values (funcall (cdr m)) t) (values nil nil))))
               ((setf valof) (n s)
                 (let ((m (assoc s #1#)))
                   (unless m (error "~S unbound" s))
                   (funcall (cdr m) n))))
          (valof 'a))))

Now everything is better:

> (let/names ((a 1))
    (valof 'a))
1
t

> (let/names ((a 1))
    (valof 'b))
nil
nil

> (let/names ((a 1))
    (setf a 2)
    (valof 'a))
2
t

> (let/names ((a 1))
    (setf (valof 'a) 3)
    a)
3

Why neither of these macros is completely right. Neither macro deals with declarations properly: in a case like

(let/names ((a 1))
  (declare (type fixnum a))
  ...)

The declaration will end up in the wrong place. Dealing with this requires 'lifting' declarations to the right place, which is easy to do but I'm not going to make these even more complicated than they already are.

ignis volens
  • 7,040
  • 2
  • 12
  • Thank you @ignis about considering such idea. And I'm afraid the point is bit different with my focus. But still, I love your tricks like `(let ... (list ,@(mapcar (lambda ...`. They're interesting and nice study/exercise for me. Thank you again! – LMB Feb 02 '23 at 15:28
0

Would something like this solve your problem?

(defmacro my-let ((&rest bindings) &body body)
  `(let ,bindings
     (let ((symbols ',(mapcar #'car bindings))
           (values (list ,@(mapcar #'car bindings))))
       ,@body)))
                                                                                                                                                                                                                                              
((lambda (bar)
   (my-let ((b bar))
     (list symbols values)))
 "baz")

((B) ("baz"))
coredump
  • 37,664
  • 5
  • 43
  • 77
  • Thank you @coredump for answering. Yes, your answer is big help for my problem (scope was also problem for me). and I see! If I only want to get evaluated bindings, I don't need to see `cadr` and only `car` is enough regarding bindings actually... Thank you again. – LMB Feb 01 '23 at 03:22