I haven't used arc in a while, but if I remember correctly it has a Common Lisp style defmacro that anyone can figure out:
(defmacro foo (args go here) `(code goes ,here))
On the other hand, Scheme has this monstrosity for defining macros that I can't describe because I never managed to keep straight in my head how it was supposed to work.
Hygiene is a red-herring. It's easy enough to achieve with a defmacro-style macro system - just use gensyms.
> Hygiene is a red-herring. It's easy enough to achieve with a defmacro-style macro system - just use gensyms.
Hygiene can also be baked-in to a nice lispy defmacro-style system; there's no reason that hygienic macros have to be scheme-style. Variable capture can be permitted with a (capture x) special form. (I like to use ^x as a shorthand for that).
At that point, macros start behaving scope-wise similarly to lexical functions, and captured variables behave kind of like dynamic variables in a function.
It suddenly occurs to me that this form of hygiene could be implemented in the expansion of let, lambda and defun forms, at which point (someone yell at me please if I'm wrong), plain-ol' defmacros would be hygienic, unless there's a type of variable capture that I'm forgetting about right now...
Ah, this has been repeated many times, but I can't help to click "reply"...
> It's easy enough to achieve with a defmacro-style macro system - just use gensyms.
Gensym cannot prevent global identifiers from being shadowed in macro use environment.
(define-macro (my-if a b c) `(if ,a ,b ,c))
(define (boo)
(let ((if list)) ; if is shadowed
(my-if 'x 'y 'z))) ; evaluates to (x y z), not y
In CL you don't need to worry much, since (a) it is Lisp-2 so variables won't shadow operators, and (b) using packages greatly reduces accidental shadowing. In Scheme the chance is much higher; you probably don't bind 'if' locally, but you can't check every user-defined global procedures the macro inserts into the output.
It is unfortunate for abstractbill to use this incorrect explanation, because hygienic macros are still a red herring - a decent module system and a bit of restraint gets you there.
(define-macro (my-if a b c) `(std.if ,a ,b ,c))
(define (boo)
(let ((if list)) ; if is shadowed
(my-if 'x 'y 'z))) ; gives (std.if x y z)
(define (boo2)
(let ((std.if list)) ; error! cannot shadow module vars.
(my-if 'x 'y 'z)))
I've posted this around a few times asking why this isn't already used in Lisps and never seem to get a convincing argument back. The only conclusion I can draw from this is that CL-ers are ashamed of the fact that multiple namespaces aren't actually necessary and Schemers are rightly ashamed of hygienic macros. Please convincingly prove me wrong!
(ps. This ofcourse would not be limited to the languages std module but would also apply to any exported module symbols.)
That's exactly what I meant in "In CL ... (b) using packages greatly reduces accidental shadowing." So I'm not sure what you are asking to be convinced. CL certainly does not prevent you from messing with package-private symbols, but usually a convention is enough to avoid disaster. I mean, I won't write (let ((your-package::private-symbol list)) (your-macro ...) ...) unless there's absolute need to do, such as emergency workaround of a bug in a third-party library.
On the other hand, one of the reasons Scheme avoids explicit module prefix is, I think, that it breaks abstraction. Suppose my-module defines my-macro and my-function. My-macro expands into a call to my-function. But I don't really want my module users to call my-function directly, so I only export my-macro and keep my-function private.
In your proposal, my-macro will expands into prefixed-symbol like this:
But... if we allow module-private function to be called with module prefix, we can't prevent ordinary code from calling my-function as well.
This is a language design choice. Some may not mind others to mess with module-private bindings; CLers certainly would not. But if you want to hide module-private symbols, and want to allow macros expand into them, how will you do that? One idea is to mark the output of macro expansion so that the compiler can distinguish whether the reference is from legitimate macro expansion or not... well, that's what hygienic macro is doing.
Thanks for the explanation. I hadn't thought of the my-macro problem because I tend to think of macros exactly as a clever copy paste mechanism, so would expect it to always use public variables, but I guess some people might have a problem with that in some cases.
In the first point I was asking to be convinced that you can't have hygiene solely through the use of a package / module system and without needing multiple namespaces.
Although your workaround argument (for allowing variables to be shadowed) is convincing, in that case the package system wouldn't really be providing any safety at all and only the convention would be helping. I guess we can sum it up as CLers really don't see a problem at all, and Schemers require macros to be completely abstract.
(mac awhen (expr . body)
`(std.let it ,expr (std.if it (std.do ,@body))))
This assumes that awhen is being defined and exported from the std module too, so that it can also be used in expansions. Slightly more verbose, but that's optional hygiene if you want it.
Hmm, I don't know if I'm going to be able to explain it very well without an example. My general experience was that I'd start with syntax-rules, which would turn out to not be powerful enough for the particular macro I wanted to write, so I'd turn to the more complex but more powerful syntax-case. Which then might turn out to not be powerful enough either, or else it might be powerful enough but I'm struggling to understand how to implement my macro using it, plowing my way through pages of documentation and reading examples.
Then there's a macro in Arc, for example,
(mac awhen (expr . body)
`(let it ,expr (if it (do ,@body))))
which, on the one hand, yes it is true it's not hygienic. But, on the other hand, I can bang out in seconds. So now I'm moving ahead writing my program instead of struggling to write a macro. And the lack of hygiene has never been an actual problem for me in practice.
I imagine that a Scheme programmer who was experienced in writing hygienic macros and so could write them quickly, and who was more bothered by the lack of hygiene than I am, could well reach a different cost/benefit decision than I do and decide they'd rather go with hygienic macros.