Arc Forumnew | comments | leaders | submitlogin
8 points by almkglor 6215 days ago | link | parent

macros are processed before anything else - before even executing anything else.

     your code
         |
         v
   macro expander
         |
         v
      Arc core
So if part of your execution is assigning the macro, then it won't work.

In your first case, this is what happens:

  arc> (set my-plus (annotate 'mac (fn (x y) `(+ ,x ,y))))
                       |
                       v
                 macro expansion
                (hmm, I don't see any macros
                 to expand... oh well!)
                       |
                       v
   (set my-plus (annotate 'mac (fn (x y) `(+ ,x ,y))))
                       |
                       v
                    Arc core
                  (okay, so I construct a fn,
                   annotate it with 'mac, then
                   store it in my-plus... done!)

                arc> (my-plus 19 71)
                        |
                        v
                   macro expansion
                  (hmm.. oh!  my-plus is a macro!
                   lemme expand that...)
                        |
                        v
                     (+ 19 71)
                        |
                        v
                    Arc core
                    (okay, so I add 19 and 71....
                     hey, the answer is 90!)
However, for your second case:

  arc>((fn () (set my-plus (annotate 'mac (fn (x y) `(+ ,x ,y)))) (my-plus 19 71)))
                                     |
                                     v
                                macro expander
                              (meh, I don't see any macros,
                               how boring...)
                                     |
                                     v
   ((fn () (set my-plus (annotate 'mac (fn (x y) `(+ ,x ,y)))) (my-plus 19 71)))
                                     |
                                     v
                                 Arc core
                           (okay, so there's this function
                            I have to execute... load
                            my-plus with some object I don't
                            know about... hey!  Why am I
                            trying to call some object I
                            don't know about??  Error hander!
                            Tell the boss there's a problem!)
                                       |
                                       v
  Error: "Function call on inappropriate object #3(tagged mac #<procedure>) (19 71)"


4 points by almkglor 6215 days ago | link

In other words: local macros don't exist. On the Anarki, there's this hacky thing in "lib/macrolet.arc" which simulates local macros, called 'maclet, 'macwith etc - be warned though, it's guaranteed to be buggy.

-----

1 point by conanite 6215 days ago | link

Awesome, thanks for the explanation. Is this a language design choice, or is there some fundamental reason that it must be so? I ask because the ((fn () ... )) case works in rainbow - but I would like to have a set of tests that behave the same way on ac.scm as well as on rainbow - this might ultimately prove useful to other arc implementors too ...

-----

2 points by almkglor 6215 days ago | link

The fundamental reason is really the problem of how to implement macros.

Most Lisp's are targetted towards compilation. So what happens is really like this:

    your code
        |
        v
   macro expander
        |
        v
     compiler
This means that if you define this code:

  (mac my-add (x y)
    `(+ ,x ,y))
  (def my-function (x y z)
    (my-add (my-add x y) z))
Then the macro expander will expand the code to

  (set my-function
    (fn (x y z) (+ (+ x y) z)))
But if we really, really wanted to have macros as first class, then how would the following code get compiled?

  (def my-function (x y)
    (my-oper x y))
  (mac my-oper (x y)
    `(+ ,x ,y))
  (pr (my-function x y))
  (mac my-oper (x y)
    `(- ,x ,y))
  (pr (my-function x y)) ;exactly the same call, completely different meaning
  (mac my-oper (x y)
    `(* ,x ,y))
  (pr (my-function x y)) ;exactly the same call, completely different meaning
If we supported macros as first-class objects, then a "compiled" program would have to compile itself while running, because the macros might have changed between invocations of the macro. In such a case, you might as well have just stuck with the interpreted version.

The problem isn't intractible (you could do JIT compilation and check if the macro inside the variable is still the same to the older macro you used), but it's not easy either. And besides, most people don't find a need to redefine macros anyway. So most Lisps just punt: the rule is, the macro exists before the Lisp reads the code.

-----

1 point by absz 6214 days ago | link

But couldn't you decide that any macro was only evaluated once (so the my-oper in my-function wouldn't change), but macros were searched for in the lexical namespace anyway? This would mean that any call with a lexical in the functional position would have to be checked for macro-expansions at runtime, of course, but it would be slightly more reasonable.

-----

2 points by almkglor 6214 days ago | link

Yes, but again: compilation during runtime. Meaning (most likely) some sort of JIT. Wanna try to implement this? You could start by hacking this onto pg's arc-to-scheme implementation.

-----

1 point by absz 6214 days ago | link

Hmm, might well do so if I can make time for it. (I can probably, soon.)

-----

3 points by almkglor 6213 days ago | link

Okay. Be careful to still be able to properly handle environments, without actually turning it into an Arc interpreter.

pg's ArcN is really an Arc-to-Scheme compiler. And I dearly suspect that this was the main reason for not implementing first-class macros. Macros are intended to work on the code before the compiler does, so having true first-class macros is a little difficult.

-----

1 point by conanite 6214 days ago | link

So if I understand well, it's a performance constraint that could be solved but, because compile-first doesn't hurt anybody anyway, isn't. Thanks!

-----