Page 1 of 1

Using barewords not keywords in macros

Posted: Fri Apr 17, 2009 8:42 am
by aerique
If been trying to figure something out and neither Google has been much help nor did looking at the source for the iterate package help me. It only made me more confused.

So I hope there's a simple answer to this: how does one use a bareword in a macro? An iterate form for example looks like this:

Code: Select all

(iter (for item in list) ...)
While if I were to make an iterate macro I would have to give it the following syntax since my CL will otherwise complain about "for" not being a function and "in" being an unbound variable:

Code: Select all

(iter (:for item :in list) ...)

Re: Using barewords not keywords in macros

Posted: Fri Apr 17, 2009 9:07 am
by findinglisp
Whenever the compiler/interpreter encounters ITER in the first position of a list, it immediately starts to process it as a macro. It passes all the rest of the macro form (everything that follows ITER) to the ITER macro, uninterpreted. Therefore, the compiler never has to determine whether FOR or IN are bound to functions or other values. In other words, everything in the body of the ITER form is just raw data at that point. Once all that is passed to ITER, the macro has a chance to examine all its parameters and return another form to replace the ITER form. The compiler/interpreter then repeats the same process on the returned form (which may also be a macro and invoke yet another macro expansion). As long as the compiler/interpreter never gets to the point where it actually tries to compile/interpret the FOR or IN forms, there is not a problem. They are simply raw symbols that are interpreted by the ITER macro at macro expansion time.

Re: Using barewords not keywords in macros

Posted: Fri Apr 17, 2009 9:10 am
by ramarren
I have never seen the word "bareword" used before and am fairly certain that it has no Lisp-specific meaning, so what do you mean exactly?

Also, iterate macro as defined in iterate library is a perfectly normal macro written in more or less portable Common Lisp, and so you can do anything it does.
aerique wrote:While if I were to make an iterate macro I would have to give it the following syntax since my CL will otherwise complain about "for" not being a function and "in" being an unbound variable:
You seem to be missing a point of macros. Macro is a function on source code, and so the symbols are passed as is, and not evaluated. In any case using keywords in function namespace wouldn't help anyway, as ":for" in not a function any more than "for".

Re: Using barewords not keywords in macros

Posted: Fri Apr 17, 2009 3:00 pm
by aerique
Ramarren wrote:I have never seen the word "bareword" used before and am fairly certain that it has no Lisp-specific meaning, so what do you mean exactly?
Basically "not a keyword". I tried to explain that with my two code examples.
Ramarren wrote:You seem to be missing a point of macros.
No, I don't :)

I just have next to no experience writing them and these are some of the issues I'm running into.
findinglisp wrote:As long as the compiler/interpreter never gets to the point where it actually tries to compile/interpret the FOR or IN forms, there is not a problem.
Ramarren wrote:Macro is a function on source code, and so the symbols are passed as is, and not evaluated. In any case using keywords in function namespace wouldn't help anyway, as ":for" in not a function any more than "for".
This is where I was having problems. However a simple test-case shows it to be as simple as you guys write:

Code: Select all

(defmacro test (&rest args)
  (dolist (arg args)
    (format t "~S, ~S~%" (type-of arg) (symbol-name arg))))

CL-USER(15): (test a b c d)
SYMBOL, "A"
SYMBOL, "B"
SYMBOL, "C"
SYMBOL, "D"
NIL
So, I have no idea where it went wrong with the macro I was working on. I don't have it at hand at the moment though :(

Re: Using barewords not keywords in macros

Posted: Fri Apr 17, 2009 3:34 pm
by aerique
Alright, figured out what went wrong. I forgot to downcase the keywords during a comparison so instead of getting filtered out they did indeed get evaluated at a later moment. I was totally focussing on the wrong part of the macro when trying to find the problem.

I hereby present Iffy!:

Code: Select all

(defmacro iffy (condition &rest args)
  (let ((state :then-or-else)
        (then-forms nil)
        (then-or-else-forms nil))
    (dolist (arg (reverse args))
      (cond ((equal (string-downcase (symbol-name arg)) "then")
             nil)
            ((and (equal state :then-or-else)
                  (equal (string-downcase (symbol-name arg)) "else"))
             (setf state :then))
            ((equal state :then)
             (push arg then-forms))
            ((equal state :then-or-else)
             (push arg then-or-else-forms))))
    (if then-forms
        `(if ,condition
             (progn ,@then-forms)
             (progn ,@then-or-else-forms))
        `(if ,condition
             (progn ,@then-or-else-forms)))))
(no worries, it was just an excercise in writing macros :lol:)

Re: Using barewords not keywords in macros

Posted: Fri Apr 17, 2009 5:24 pm
by findinglisp
Rather than dealing with case conversion, you could simply call STRING-EQUAL which ignores case. Thus:

Code: Select all

(STRING-EQUAL "foo" "FOO")
     => T
In your case, you'd do

Code: Select all

(string-equal (symbol-name arg) "then")

Re: Using barewords not keywords in macros

Posted: Fri Apr 17, 2009 11:27 pm
by gugamilare
aerique wrote:Alright, figured out what went wrong. I forgot to downcase the keywords during a comparison so instead of getting filtered out they did indeed get evaluated at a later moment. I was totally focussing on the wrong part of the macro when trying to find the problem.
All right, it looks like you are getting the hang of macros. Don't worry, these details confuse sometimes, specially in the beginning. :D

Just a hint, if you are trying to see if the name of a keyword is "then", you don't need to compare strings. That's what symbols (and keywords, which are also symbols) are for. So, instead of:

Code: Select all

(equal (string-downcase (symbol-name arg)) "then")
you can just

Code: Select all

(eq arg :then)
Or you can even use case:

Code: Select all

(case arg
  (:then ...)
  (:else ...))

Re: Using barewords not keywords in macros

Posted: Mon Apr 20, 2009 1:00 pm
by findinglisp
gugamilare wrote: Just a hint, if you are trying to see if the name of a keyword is "then", you don't need to compare strings. That's what symbols (and keywords, which are also symbols) are for. So, instead of:

Code: Select all

(equal (string-downcase (symbol-name arg)) "then")
you can just

Code: Select all

(eq arg :then)
Or you can even use case:

Code: Select all

(case arg
  (:then ...)
  (:else ...))
This works with keywords, but will have problems if you want to use non-keyword symbols, because of problems with which package a given symbol is interned in. The OP is doing it correctly if he wants something more tolerant like the ITERATE syntax where it allows you to use either "THEN" or ":THEN" in the particular DSL you are creating. In this case, you're essentially just treating the symbol as a string and so it's appropriate to compare the symbol name. This avoids a wide range of possible problems. Besides, if this only runs at macro expansion time, it probably isn't performance sensitive.