and then use test as a parameter name in some unrelated fn:
(def whatever (test xs) (test (car xs) ...))
That seems pretty bad that they share the same namespace. Maybe this is an isolated case. Is there a good rule-of-thumb for avoiding this? Besides "be careful with macro names" or "be careful with parameter names?
In my code, I've been doing sorta convoluted things to avoid accidental macro-expansion just like what you're talking about:
(def whatever (row) (row "something")) ; unsafe
(def whatever (row) (do.row "something")) ; safe
(def whatever (row) (or.row "something")) ; safe
; The "or.row" one is possibly more efficient, since it expands to
; "row", but I use "do.row" 'cause I don't want to desensitize myself
; to "or".
(def whatever (tab key) (= tab.key "something")) ; unsafe
(def whatever (tab key) (= do.tab.key "something")) ; doesn't work
(def whatever (tab key) (= or.tab.key "something")) ; confuses me
(def whatever (tab key) (= .key.tab "something")) ; safe
As long as I keep this up, my macros and parameters can conflict all they like, and it doesn't matter. But this approach basically amounts to qualifying the name of each parameter as it's used in function position, so from my point of view, macros and parameters almost don't live in the same namespace.
If you look at the Arc compiler around line 448 in ac.scm from Arc 3.1, you can see that the first thing it does it is check whether the "fn" in the first position of a "call" form is a macro:
that's why an identifier being a macro takes precedence over an identifier being a lexical variable.
But you can easily change it. The "lex?" function will tell you whether an identifier is a lexical variable at that point. So you can change the test in the cond for "macfn" to something like "(and macfn (not (and (symbol? fn) (lex? fn env))))" and an identifier being a lexical will take precedence over an identifier being a macro.
It seems to me that a lexical binding should override a global macro binding, no matter what. I don't see a reason not to do it that way, except possibly that it would make it harder to handle macroexpansion (it adds another special case to macroexpansion, along with quoted/quasiquoted forms and parameter lists and wherever else macros shouldn't be expanded). But we already have a full code-walker in ac.scm, and aw has shown us just where to look and what to do, so this turns out not to be much of a problem.
And it turns out that both Common Lisp and Scheme agree with me, that lexical bindings should override global macro bindings. As we see here (pardon my choice of throwaway function name; it's short and the computer doesn't mind):
> (define-macro (ass x) ;Scheme
`(list ,x 2))
> (let ((ass (lambda (x) (+ x 1))))
(ass 1))
2
* (defmacro ass (x) `(list ,x 1)) ;CL
ASS
* (flet ((ass (x) (+ x 1))) (ass 2))
3
In the above examples, if the calls to 'ass got expanded as macros, then what would be returned is some kind of list, rather than an integer. It's only in Arc that we get:
arc> (mac ass (x) `(list ,x 1))
#(tagged mac #<procedure: ass>)
arc> (let ass (fn (x) (+ x 1)) (ass 2))
(2 1)
By the way, in order to format the above code (add 2 spaces before each line), I used the following handy little 'clipboard function after copying the interactions with the REPLs. I think pbpaste works on all Unix-like systems:
Your 'whatever function rebinds 'test only for the lexical scope of the function definition. After defining the macro and then the function as you have, 'test is still bound to the macro:
arc> (mac test (name actual compare-fn expected)
"test macro")
#(tagged mac #<procedure: test>)
arc> (def whatever (test xs)
test)
#<procedure: whatever>
arc> test
#(tagged mac #<procedure: test>)
arc> (test nil nil nil nil)
"test macro"
The binding of function parameters behaves like 'let in this regard, affecting a variable's value within the lexical scope of the block but producing no side effects on it outside.
We can even pass 'test as a parameter to 'whatever and it will retain its original value as a macro, because the rebinding of 'test as a parameter was only for the function's definition, not for a call to that function:
arc> (whatever test nil)
#(tagged mac #<procedure: test>)
I thought the point of 'baz was to call the first parameter with the second parameter. That is, the result of (baz "abc" 0) would be the character #\a if not for that meddling macro. Your 'baz1 is totally different.
BTW, here's my version of 'baz: (def baz (m index) do.m.index)