let vs setf in regards to macros

Discussion of Common Lisp

let vs setf in regards to macros

Postby conroe64 » Tue Jan 10, 2012 1:08 pm

In the following code, the foo-mac macro will read the current value of the var *b* as set by a setf, but not as set by using a let. However, foo-fun will read the current value of *b* regardless if using a setf or a let.

What I would like to do is use macros and a nested structure such as the let's in the third example, below, and have the macros read the *b* var as set by the let's. Is there an elegant way to do this? I suppose I could roll my own nested construct to do this relying on a stack and setf, but I was wondering if there is something part of lisp that already does this. I am also curious as to why lisp behaves differently in regards to setf vs let in association to macros (but this isn't really that important)

Thanks in advance.

Code:
Code: Select all
(defvar *b*)

(defmacro foo-mac (name)
  `(format t "mac ~A: ~S~%" ,name ',*b*)
  )

(defun foo-fun (name)
  (format t "fun ~A: ~S~%" name *b*)
  )

;;works as expected
(setf *b* 'x)
(foo-mac "setf")
(foo-fun "setf")

;;*b* isn't set for macro foo-mac, but is for function foo-fun
(let ((*b* 'b))
  (foo-mac "let")
  (foo-fun "let")
  )

;;what I really want to do is set vars in a nested way, such as this:
(let ((*b* 'b1))
  (foo-mac "let1")
  (foo-fun "let1")
  (let ((*b* 'b2))
    (foo-mac "let2")
    (foo-fun "let2")
    )
  (foo-mac "let1")
  (foo-fun "let1")
  )


Output, from SBCL version 1.0.19-gentoo:
Code: Select all
mac setf: X
fun setf: X
mac let: X
fun let: B
mac let1: X
fun let1: B1
mac let2: X
fun let2: B2
mac let1: X
fun let1: B1
conroe64
 
Posts: 3
Joined: Sun May 15, 2011 7:05 pm

Re: let vs setf in regards to macros

Postby gugamilare » Tue Jan 10, 2012 2:26 pm

How about this?

Code: Select all
(defmacro foo-mac (name)
  `(format t "mac ~A: ~S~%" ,name *b*))


With this code, you get the value of *b* at evaluation time, while, with your code, you get the value of *b* at macro-expansion time.
gugamilare
 
Posts: 406
Joined: Sat Mar 07, 2009 6:17 pm
Location: Brazil

Re: let vs setf in regards to macros

Postby Kompottkin » Tue Jan 10, 2012 2:29 pm

The problem is not with setf versus let. The problem is that foo-mac will directly insert the value of *b* at macro expansion time into the generated code as a literal.

To illustrate this:

Code: Select all
CL-USER(10): (defvar *b* 'a)
*B*
CL-USER(11): (funcall
              (lambda ()
                (setf *b* 'x)
                (foo-mac "setf")
                (foo-fun "setf")))
mac setf: A
fun setf: X
NIL
User avatar
Kompottkin
 
Posts: 94
Joined: Mon Jul 21, 2008 7:26 am
Location: München, Germany

Re: let vs setf in regards to macros

Postby conroe64 » Fri Jan 13, 2012 12:55 am

gugamilare wrote:How about this?

Code: Select all
(defmacro foo-mac (name)
  `(format t "mac ~A: ~S~%" ,name *b*))


With this code, you get the value of *b* at evaluation time, while, with your code, you get the value of *b* at macro-expansion time.


I actually need it during macro-expansion time. To be more precise, I'm trying to automatically create functions using a macro which use a var to specify their behavior. Like this:
Code: Select all
(defvar *b*)

(setf *b* nil)

(defmacro foo-mac (func-name)
  `(defun ,func-name () (format t "~A: ~S~%" ',func-name ',*b*))
  )

(let ((*b* 'x))
   (foo-mac the-x-function)

   (let ((*b* 'y))
      (foo-mac the-y-function)
      )

   (foo-mac the-other-x-function)
)

(the-x-function)
(the-y-function)
(the-other-x-function)


What I want is
THE-X-FUNCTION: X
THE-Y-FUNCTION: Y
THE-OTHER-X-FUNCTION: X

but what I get is:
THE-X-FUNCTION: NIL
THE-Y-FUNCTION: NIL
THE-OTHER-X-FUNCTION: NIL
conroe64
 
Posts: 3
Joined: Sun May 15, 2011 7:05 pm

Re: let vs setf in regards to macros

Postby Kompottkin » Fri Jan 13, 2012 3:27 am

LET works at run-time, not at macro expansion time, so you can't do it that way.

There are at least three possible solutions:

  • Use run-time code instead of a macro. Use COMPILE instead of DEFUN.
  • Use MACROLET instead of LET.
  • Expand into code that closes over *B* by rebinding it lexically.

The first approach looks like this:

Code: Select all
(defvar *b*)

(defun foo (func-name)
  (compile func-name
           `(lambda ()
              (format t "~A: ~S~%" ',func-name ',*b*))))

(let ((*b* 'x))
  (foo 'the-x-function)
  (let ((*b* 'y))
    (foo 'the-y-function))
  (foo 'the-other-x-function))

(the-x-function)
(the-y-function)
(the-other-x-function)


This is an implementation of the second approach:

Code: Select all
(defmacro foo (func-name &environment env)
  `(defun ,func-name ()
     (format t "~A: ~S~%" ',func-name ',(macroexpand '(*b*) env))))

(macrolet ((*b* () 'x))
  (foo the-x-function)
  (macrolet ((*b* () 'y))
    (foo the-y-function))
  (foo the-other-x-function))

(the-x-function)
(the-y-function)
(the-other-x-function)


Finally, the third approach:

Code: Select all
(defvar *b*)

(defmacro foo (func-name)
  (let ((b (gensym)))
    `(let ((,b *b*))
       (defun ,func-name ()
         (format t "~A: ~S~%" ',func-name ,b)))))

(let ((*b* 'x))
  (foo the-x-function)
  (let ((*b* 'y))
    (foo the-y-function))
  (foo the-other-x-function))

(the-x-function)
(the-y-function)
(the-other-x-function)
User avatar
Kompottkin
 
Posts: 94
Joined: Mon Jul 21, 2008 7:26 am
Location: München, Germany


Return to Common Lisp

Who is online

Users browsing this forum: No registered users and 5 guests

cron