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)"
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.
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 ...
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.
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.
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.
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.