Arc Forumnew | comments | leaders | submit | ulix's commentslogin
1 point by ulix 5499 days ago | link | parent | on: Macros without 'mac'?

You can get around the problem also in the following "weird" way:

(let x ''(+ 1 2) (xrepeat 3 `(pr ,x)))

but the way you proposed is more "natural" since it respects the "rule" of quoting functions and variables passed at calling time.

Am i wrong if I think that, in the end, the main difference between 'def'+'eval' and 'mac' is that 'mac' allows its code to be evaluated and expanded once, whereas 'def' needs to expand its code every time it is called ?

-----

2 points by rocketnia 5499 days ago | link

Am i wrong if I think that, in the end, the main difference between 'def'+'eval' and 'mac' is that 'mac' allows its code to be evaluated and expanded once, whereas 'def' needs to expand its code every time it is called ?

It's 'eval that expands its code once every time it's called. If you don't use 'eval at all, then all your arc code will be expanded exactly once, at the time you enter it at the REPL or load it from a file (since those are the points where 'eval is called internally).

When you enter the expression (def foo (x) (obj key x)) is at the REPL, it's evaluated as a two-step process: First it's compiled (which expands the (def ...) and (obj ...) macro forms), and then the compiled code is run. When it's run, the body of the function isn't run yet; instead, it's packed up into a procedure and stored as foo. When you call foo later, no expansion takes place, only running of already compiled code.

A better (but not perfect) way to use 'eval equivalently to 'mac is something like this:

  (def mywhen (test . body)
    `(if ,test (do ,@ body))
  
  (eval `(time:for i 1 1000 ,(mywhen '(is i 666) '(prn "Oh no!"))))
If we wanted to make 'do a non-macro, we could do that too:

  (def mydo body
    `((fn () ,@body)))
  
  (def mywhen (test . body)
    `(if ,test ,(apply mydo body)))
  
  (eval `(time:for i 1 1000 ,(mywhen '(is i 666) '(prn "Oh no!"))))
Finally, if we wanted to avoid the use of macros altogether, the example would turn into something like this:

  ; The procedure corresponding to an Arc macro can already be obtained
  ; using 'rep.
  (assign mywhen rep.when)
  (assign mytime rep.time)
  (assign myfor rep.for)
  
  (eval:mytime:myfor 'i '1 '1000 (mywhen '(is i 666) '(prn "Oh no!")))
I could make a few guesses as to why this kind of programming isn't popular in lisps, but my personal reason is that I tend to consider all operators to provide their own syntax, with procedure call syntax just being the default choice. For me, it's inconsistent to have the '(prn ...) syntax be quoted when the (mywhen ...) syntax isn't.

-----

1 point by ulix 5498 days ago | link

Very interesting, thanks!

Do you know where I can find an Arc language reference?

-----

1 point by aw 5498 days ago | link

http://arcfn.com/doc/index.html and http://arcfn.com/2009/06/whats-new-in-arc3.html

-----

1 point by ulix 5499 days ago | link | parent | on: Macros without 'mac'?

Thank you for the answer, very interesting. Here's some more things I found:

1) actually the "reference to undefined identifier" error can be avoided un-quoting the variables at calling time, as in: (let i 2 (eval­ `(+ ,i 1))) (time:for i 1 1000 (mywh­en `(is ,i 666) '(prn­ "Oh no!")))

2) the last expression is computed in 176 msec, whereas the regular macro 'when' is computed in 2 msec (much faster as you forecasted).

3) these examples don't work in online interpreter Arc Web REPL (http://dabuttonfactory.com:8080/), but they work in TryArc (http://tryarc.org/)

Thanks

-----