Arc Forumnew | comments | leaders | submitlogin
3 points by fallintothis 5204 days ago | link | parent

1. Not exactly, but it helps to think of it that way.

  (mac foo (x)
    (list 'bar x))

  (foo abcdef)
is conceptually like

  (eval ((fn (x) (list 'bar x)) 'abcdef)) ; = (eval (list 'bar 'abcdef))
Notice how the argument 'abcdef was quoted. Macros don't evaluate their arguments, but the code they generate might (e.g., if bar was a function, it'd try to evaluate abcdef as a variable).

They aren't actually implemented that way. eval operates at run-time and doesn't have access to lexical variables. Macros expand at compile-time, so it's as if you had the expansion there to begin with. E.g.,

  arc> (let y 10
         (eval 'y))
  Error: "reference to undefined identifier: _y"
but

  arc> (mac foo (arg) arg)
  #3(tagged mac #<procedure: foo>)
  arc> (let y 10
         (foo y))
  10
because

  (let y 10
    (foo y))
macroexpands into

  (let y 10
    y)
before it ever runs.

Anonymous macros are plausible, but they might force work to be done at run-time -- essentially, you're right that eval's the closest thing to it. But since macros happen at compile-time, you can't do something like

  ((if (mem 'x stuff) push pull) 'y stuff)
The compiler sees the macros push and pull, but they're not applied to anything, so it doesn't expand them. Then at run-time, you get an error as it tries to evaluate each form. You have a macro trying to act like a function (i.e., at run-time).

This topic comes up every so often: http://arclanguage.org/item?id=11517.

2. do is for when you want to execute a series of expressions, but do it in just one s-expression (i.e., the thing wrapped in do). You see it a lot in macros; e.g., defs in arc.arc:

  (mac defs args
    `(do ,@(map [cons 'def _] (tuples args 3))))
It converts

   (defs f (x) (+ x 1)
         g (y) (- y 1))
into

   (do (def f (x) (+ x 1))
       (def g (y) (- y 1)))
which is just one list. You couldn't return multiple values, so the macro couldn't expand into

  (def f (x) (+ x 1))
  (def g (y) (- y 1))
directly.

Another place you see do a lot is in if statements. Each clause is one expression, but if you want to do multiple things in one expression, you need the do. E.g.,

  (if test
      (do (pr #\t) (pr #\h) (pr #\e) (pr #\n))
      else)
will print "then" if test is true, otherwise it'll do else. This wouldn't work without the do:

  (if test       ; if "test"
       (pr #\t)  ; print #\t
      (pr #\h)   ; else if (pr #\h)
       (pr #\e)  ; then (pr #\e)
      (pr #\n)   ; else if (pr #\n)
       else)     ; then "else"
P.S. That's the return value of the statement.

  arc> (pr "a") ; prints "a" WITHOUT a newline, then returns the first thing it
                ; printed (the string "a")
  a"a"
  arc> (prn "a") ; print "a" WITH a newline, then returns the first thing it
                 ; printed (the string "a")
  a
  "a"
To learn more about macros, my debugger tool might be helpful: http://arclanguage.org/item?id=11806. Let me know if it is!