Arc Forumnew | comments | leaders | submitlogin
Macro question
2 points by d0m 5020 days ago | 13 comments
Is it possible to add keyword in a macro declaration?

For example:

Instead of using:

(foreach x seq)

I would like to use:

(foreach x in seq)

note:

I know I can do it with: (foreach x 'in seq) and use (is 2nd-arg 'in).



4 points by evanrmurphy 5020 days ago | link

I know I can do it with: (foreach x 'in seq) and use (is 2nd-arg 'in).

This is probably the most intuitive approach, but since foreach is a macro, you don't need to quote in.

  arc> (mac foreach (var in expr . body)
         `(each ,var ,expr
            ,@body))
  #(tagged mac #<procedure: foreach>)

  arc> (foreach x in '(a b c)                ; in, not 'in
         (prn x))
  a
  b
  c
  nil
My definition is silly because it doesn't do anything with the in arg. It doesn't even require that the arg be in!

  arc> (foreach x fruitloops '(o o o)
         (prn "yum"))
  yum
  yum
  yum
  nil
More interesting things are certainly possible, though. :)

-----

2 points by d0m 5020 days ago | link

Oh wow, thank you. For some reasons, I thought I couldn't just write (var in expr. body) since in the call:

(foreach x in '(..)), "in" would have been evaluated to an unknown symbol.. However, I was totally wrong since I don't even have to evaluate it! :)

-----

1 point by d0m 5020 days ago | link

Let's suppose however that I want to check if it is really in and not fruitloops.. Is there a way?

For example:

(foreach x in seq (prn x)) -> call (each x seq (prn x))

(foreach x seq (prn x)) -> call (each x seq (prn x))

(foreach x fruitloops seq (prn x)) -> (prn "mmmm")

-----

1 point by evanrmurphy 5020 days ago | link

It's not pretty, but you could do:

  (mac foreach (var . args)
    (if (is car.args 'in)
          `(each ,var ,(cadr args)
             ,@(cddr args))
         (is car.args 'fruitloops)
          `(each ,var ,(cadr args)
             (prn "yum"))
         (acons car.args)
           `(each ,var ,(car args)
              ,@(cdr args))))
Update: I'm not sure it's any more clear, but another option would be,

  (mac foreach (var . args)
    `(each ,var ,(if (in car.args 'in 'fruitloops)
                      cadr.args
                     car.args)
       ,@(case car.args
           'in         cddr.args
           'fruitloops `((prn "yum"))
                       cdr.args)))
except that it's not working correctly for the 'fruitloops case and I can't figure out why.

Upperdate: Aha! It's because in and fruitloops in the case statement should not be quoted:

  (mac foreach (var . args)
    `(each ,var ,(if (in car.args 'in 'fruitloops)
                      cadr.args
                     car.args)
       ,@(case car.args
           in         cddr.args                       ; in not quoted
           fruitloops `((prn "yum"))                  ; ditto fruitloops
                      cdr.args)))
This works.

-----

1 point by akkartik 5020 days ago | link

The convention I use is to begin keywords in function calls with a colon.

  (mac foreach (var :in expr . body)
    ..

-----

1 point by d0m 5020 days ago | link

Doesn't it make a little bit less pretty?

(foreach x in '(1 2 3)) vs (foreach x :in '(1 2 3)) ?

I'm sure you have good reasons to use this convention.. mind sharing them?

-----

2 points by evanrmurphy 5020 days ago | link

I think the colon is his convention for within the definition, as a visual marker to distinguish keyword parameters from others. When you later call a function or macro defined as such, you won't need to prepend the colon to arg you're passing (similar to how mine was called in within the definition but could be fruitloops when I called it).

Edit: On second thought, probably does use the colon when calling the function as well.

-----

3 points by shader 5020 days ago | link

The colon is a standby from other lisps; Common Lisp would intern symbols starting with : to a special KEYWORD symbol table.

That was important because you could do things like keyword based arguments to functions, etc. i.e. if you had a function that was defined as

  (defun greet (&key (a "Hello") (b "World")) 
    (print a " " b))
then you could call it like:

  >(greet)
  Hello World
  >(greet :b "Bob")
  Hello Bob
  >(greet :b "Alice" :a "Yo")
  Yo Alice
etc. As you can see, it's a lot more flexible than "traditional" optional arguments, since you can specify any of them, and in any order.

-----

1 point by akkartik 5019 days ago | link

Yes I've been using the : in calls as well, but as I wrote the comment it occurred to me that I didn't have to. Either works.

-----

2 points by akkartik 5019 days ago | link

The prettiness wasn't a concern; as programmers we're used to needing hyphens or underscores, to ignoring parentheses and focusing on indentation. The colon's like that.

-----

1 point by zck 5020 days ago | link

I'm sure you know this, but using in this way is reminiscent of Common Lisp's loop : http://cl-cookbook.sourceforge.net/loop.html

-----

1 point by evanrmurphy 5020 days ago | link

Are there keywords besides in that you're hoping to use with foreach, or did you just want in so that it reads more like English?

-----

1 point by d0m 5020 days ago | link

In this case, it was only in so it reads more like english. But, I was interested in the more general idea about how to add keyword in macro.

-----