Home > Blockchain >  Getting value in a let-binded list (Common Lisp)
Getting value in a let-binded list (Common Lisp)

Time:02-01

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.

CodePudding user response:

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"))

CodePudding user response:

[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.

  • Related