Page 1 of 1

between defun and defmacro

Posted: Mon Apr 06, 2009 2:53 pm
by greenbeer
Hello!
Is it possible in common lisp to write macro which interprets part of arguments like defmacro and other part like defun?
For example, something like:

Code: Select all

(def-amazing-macro leopard (dog &value cat)
  `(if ,dog "war is over" cat))

(let ((x "happy old year"))
  (leopard  (> 4.99 5) x))
;; it should give:
"happy old year"
PS1: i ask you for fun not for studies ;)
PS2: Of course it is easy to write a small language "embedded" in lisp in with it is possible, but I'm looking for more lisp-conventional solution.
Thanks.

Re: between defun and defmacro

Posted: Tue Apr 07, 2009 9:16 am
by qbg
In this case,

Code: Select all

(defmacro leopard (dog cat)
  `(if ,dog "war is over" ,cat))
will do what you want here:

Code: Select all

(let ((x "happy old year"))
  (leopard  (> 4.99 5) x))
;; it should give:
"happy old year"
Though it only works because you don't need to know anything about the variable 'x' at macroexpansion time.

Re: between defun and defmacro

Posted: Tue Apr 07, 2009 11:03 am
by gugamilare
If you want a shorthand so that instead of writing ",cat" you can write "cat", well, as an option your macro can be macroexpanded into something like this:

Code: Select all

(defmacro leopard (dog cat*)
	   (let ((catg (gensym)))
	     `(let ((,catg ,cat*))
		,(subst catg 'cat
			`(if ,dog "war is over" cat)))))
This is not hygienic at all; maybe you can do better than me.

OTOH, if you are thinking about evaluating the value of the variable "cat" in such a way that the macroexpansion itself can't be done without this value explicitly, then I believe the only way would be to use eval. But this would be just ugly. See http://groups.google.com/group/comp.lan ... f8fa56951a

Re: between defun and defmacro

Posted: Tue Apr 07, 2009 2:12 pm
by Paul Donnelly
greenbeer wrote:Hello!
Is it possible in common lisp to write macro which interprets part of arguments like defmacro and other part like defun?
For example, something like:

Code: Select all

(def-amazing-macro leopard (dog &value cat)
  `(if ,dog "war is over" cat))

(let ((x "happy old year"))
  (leopard  (> 4.99 5) x))
;; it should give:
"happy old year"
No matter what the value of CAT is, you've typed the symbol in a quoted list. Whether CAT is evaluated at macroexpansion time or at run time, you haven't referred to it anywhere. Quasiquoting doesn't have anything to do with macros. It's just shorthand that lets you avoid writing LIST and ' a bunch of times. If you'd written your macro properly, it would behave like you want it to.

Code: Select all

(defmacro leopard (dog cat)
  `(if ,dog "war is over" ,cat))

(macroexpand '(leopard (> 4.99 5) x)) => (IF (> 4.99 5) "war is over" X)
This gives the answer you wanted.

If you just want to ensure that CAT is only evaluated once in the macroexpanded code, or is always evaluated, then return `(LET ((CAT ,CAT)) (IF ,DOG "war is over" CAT)). Note that if the code in DOG refers to any variables named CAT, that binding will be clobbered. You can fix that with a gensym.