Arc Forumnew | comments | leaders | submitlogin
2 points by waterhouse 5205 days ago | link | parent

The most direct transformation from your attempted code to working code is this:

  (each name '(html title head body div span p h1 h2 h3 h4 i em strong b)
     (eval `(def-tag ,name)))


1 point by hasenj 5204 days ago | link

ok, now can that be encapsulated into a macro? say a macro called apply-mac-to-list

  (= tag-list '(html title head body))
  (apply-mac-to-list def-tag tag-list)
I managed to come up with this:

  (mac apply-mac (mac-name args)
      (prn:string "applying " mac-name " to " args)
      `(each arg ,args
          (eval `(,',mac-name ,arg))))
The pattern of commas and quotes is rather confusing and seems arbitrary

I got there by replacing 'eval' with 'prn', and then I kept playing with commas and quotes until it printed the right thing, then I put eval back in.

So it seems to work but I have no idea why.

EDIT:

I'm not sure if that sort of thing is common in lisp/arc? Am I approaching macros in a totally wrong way?

I presume I'm thinking in python, where a lot of meta programming techniques constitute of taking strings and using them to get fields that would normally be accessed using the symbol directly.

  getattr(obj, 'field') # same as obj.field
and so we get into the habit of doing things like:

  def apply_to_fields(obj, field_name_list, fn):
    for field in field_name_list:
        new_value = fn(getattr(obj, field))
        setattr(obj, field, new_value)

-----

1 point by waterhouse 5202 days ago | link

I see you've encountered the strange ,',name idiom. I congratulate you for writing it yourself... I read about it in "On Lisp", in its description of an "abbrev" macro. The whole book is pretty good to read through, and it says a good deal about macros, so I would recommend you take a look; but section 16.1 explains directly how one can come up with such a thing:

  http://www.bookshelf.jp/texi/onlisp/onlisp_17.html#SEC109
    The full book is there, but if you want it as a pdf or
  something, see Paul Graham's web site:
  http://paulgraham.com/onlisptext.html
Also, note that there are functions called macex and macex1. It stands for "macro-expand". They work on any macros, including ones you've just defined; they are very useful for debugging macros. See:

  arc> (macex1 '(let x 1 (+ x 2)))
  (with (x 1) (+ x 2))
  arc> (macex1 that)
  ((fn (x) (+ x 2)) 1)
Meanwhile, regarding the problem at hand. First, note that, since you're already using eval, 'apply-mac may as well be a function, defined and used like this:

  (def apply-mac (mac-name args)
    (each arg args
      (eval `(,mac-name ,arg))))
  ;Usage
  (= tag-list '(html title head body))
  (apply-mac 'def-tag tag-list)
Second, there's a little side issue brought up by your definition of 'apply-mac. It binds the variable 'arg, which... turns out to be harmless, but in general I would use a unique name for 'arg:

     `(each arg ,args
         (eval `(,',mac-name ,arg))))
  ;Rewrite as:
     (w/uniq garg
        `(each ,garg ,args
            (eval `(,',mac-name ,,garg))))
(Oh man, nested backquotes... I haven't dealt with those for a while. Last time was probably when I wrote 'abbrev.) Now that I've given you a link to On Lisp, I'm going to cut myself off and point you to chapter 9 ("Variable Capture") for further discussion. If I said more, I'd just be telling you what you could read about in On Lisp.

Regarding general practices in Lisp (you ask about how you're approaching macros)... I can say a few things. First, know that eval is slow (it performs full syntactic analysis), and you most likely should not be using it at runtime. Second, using eval is almost always unnecessary; you could use a macro or something else, and if you can't see how, then that's probably due to insufficient macro-fu. Exceptions are when the code you want to generate and evaluate actually depends on stuff that happens at runtime (e.g. your program reads Arc expressions, evaluates them, and prints the results).

In this case, this code just sets up a bunch of definitions; it's run once and it takes an imperceptible amount of time anyway. Performance isn't an issue. I would probably write it as the one-liner "(each x tag-list (eval `(def-tag ,x)))" when I was sitting at the REPL just wanting to get code working; then I'd probably write up the 'def-tags macro in my actual code file. Here's my preferred version (paraphrasing aw):

  (mac def-tags tags
    `(do ,@(map (fn (name) `(def-tag ,name)) tags)))
I think it's overkill to create "apply-mac". The concept is complicated enough and rare enough that giving it a name doesn't do much for me. I think it's best to just write def-tags using basic Arc operators.

Finally, you ask whether this sort of thing is common. The thing you said before that was "It seems to work but I have no idea why", so I'll assume that's what "that sort of thing" refers to. My answer: Generally, no. Sometimes I'll get something working by brute force search, as you seem to have done; and sometimes I'll get something working by pasting it in from someone else's code; but in either case, I will think about it and verify that it is correct and should work, and usually I'll come away satisfied in my understanding.

Note:

  (let name 'BOB
     `(meh `(ach ,',name)))
  -->
  (meh `(ach ,'BOB))
  -->
  (meh (ach BOB))
The innermost ",name" gets replaced with the value of the variable 'name; the other comma still exists, and without the quote, you would get "(meh `(ach ,BOB))", which would try to evaluate BOB. That is just how nested backquotes are defined to work; an informal introduction to them usually doesn't cover nested cases. Then, the second time around, the expression 'BOB within the quasiquote gets evaluated (because it's unquoted by the comma), and it evaluates to the symbol BOB.

-----

1 point by hasenj 5201 days ago | link

thanks for the elaborate explanation :)

> I see you've encountered the strange ,',name idiom. I congratulate you for writing it yourself

cool :D

What I meant by whether "things like this" are common, I was referring to confusing (and seemingly arbitrary) pattern of weird symbols that's not in line with the spirit of clean, readable code that's meant for humans to read instead of being meant for machines to execute.

-----

1 point by aw 5203 days ago | link

The pattern (comma quote comma)

  ,',X
turns out to be common in nested quasiquotation expressions, such as when you have macros defining macros.

See http://repository.readscheme.org/ftp/papers/pepm99/bawden.pd... for a description:

"The value of X will appear as a constant in the intermediate quasiquotation and will thus appear unchanged in the final result."

-----