I am admittedly still a Lisp (et. al.) rookie, but isn't the entire point of Scheme that it introduces hygienic macros? Or are you referring to some other (perhaps sarcastic) notion of macro hygiene?
What problems do you run into? I'm familiar with lisp's macro system but didn't get to look into hygienic macros in other language, and was wondering if they are equivalent.
Abstract: It is known that the essential ingredients of a Lisp-style unhygienic macro system can be expressed in terms of advanced hygienic macro systems. We show that the reverse is also true: We present a model of a core unhygienic macro system, on top of which a hygiene-compatible macro system can be built, without changing the internals of the core macro system and without using a code walker. To achieve this, the internal representation of source code as Lisp s-expressions does not need to be changed. The major discovery is the fact that symbol macros can be used in conjunction with local macro environments to bootstrap a hygiene-compatible macro system. We also discuss a proof-of-concept implementation in Common Lisp and give historical notes.
The code is in Figure 1, on page 10 of the PDF (p. 280 in the actual journal)
You can do cool stuff with unhygienic macros, however, like anaphoric macros. Interested readers should check out On Lisp by Paul Graham, as well as Let Over Lambda by Doug Hoyte.
Hygienic macros are a bigger deal in Scheme, where everything is conflated into single namespace (you know, the land of LST). His rant about macros and namespaces is a good hint he didn't practice Lisp that much.
Lisp-2's have less potential for trouble with unhygienic macros, but they still have it. Hygienic macro systems work with Lisp-1 as well as with Lisp-2.
Correct me if I'm wrong, but Lisp-2 vs. Lisp-1 has nothing to do with hygiene, it just splits Scheme's single problem (lexically binding values in macros) into two problems (lexically binding values in macros, and lexically binding functions in macros).
The real problem, which Naggum includes, is the lack of GENSYM (if scheme indeed lacks it), and lack of first class symbols, as he mentioned.
Hygienic macros and a strict phase separation are not distinctive to scheme, many languages have this now, most importantly Rust. And just like Rust macros scheme macros are not really an organic part of the language but some extra edifice bolted on top. Scheme definitely deserves credit for pioneering work here, but the only aspect that's of enduring distinctiveness that I'm aware of is Racket's #lang, which basically gives you a less messy and more powerful version of what you could do in Common Lisp with macros and read-tables.
My impression is that hygiene itself (which the scheme community tended to obsess over) is of minor practical benefit, but the fact that you get good error locations (because not using plain lists and symbols makes it easy to carry sufficient contextual information around[^1]) is a major upside.
Out of curiosity, are there additional important practical benefits you see, macro-wise, over Common Lisp (that would make up for the gimped repl)? I.e. in addition to better error messages?
[^1] I seem to remember being told Allegro Common Lisp does a good job here, but I assume identity still imposes some major limitations.
Some Lisp advocates may tell you that Lisp even does not have hygienic macros. Lisp dialects like Scheme have. Lisp usually has procedural macros, which transform source code (in the form of nested lists), not ASTs (like Nim).
That Nim has 'powerful hygienic macros' is fine, many languages have powerful macros.
The hygiene problem has multiple aspects. One issue is that the user's code can bind identifiers which the macro expansion expects to be predefined.
In a Lisp-1, we have to worry about this:
(let ((list ...))
(mac ...)) ;; expansion of mac wants to call (list ...)
Since the macro is not defining anything (its expansion is not introducing a binding for list), the ordinary gensym approach is not applicable.
THe problem persists into a Lisp-2 like Common Lisp, in this form:
(flet ((list (...) ....)) ;; binding in function namespace
(mac ...)) ;; wants to use (list ...)
Since local functions are rarer than local variables, and extra care can be taken in how hey are named, Lisp-2 mitigates the problem. Common Lisp goes a step further and makes it undefined behavior if code redefines a standard function (lexically or otherwise).
Hygienic macros solve this aspect of the issue as well. A hygienic (mac ...) can use (list ...) in its expansion. The hygiene mechanism ensures that this calls that list which is lexically visible at the macro definition (probably the global one in the library) even if the target site shadows list with its own definition.
The unhygeinic part isn't as important as the fact that CL's macros (and other macro systems) are not simple pattern matching and substitution. But the lack of hygiene completes the circle for the concept that the program is simply a data structure, and macros are just imperative functions from syntax to syntax, and they can do anything that a normal function can do, like make HTTP requests.
The destruction of the distinction between your code and the compiler, and the recursive relationship between the reader (and CL has reader macros, another dimension), macroexpander, and evaluator, is what is so mind-opening about lisp to me, and CL embodies that trifecta with the most ideological purity (imo).
My example is intended to be nebulously "Lisp". I did mean to mention in my comment that Common Lisp's packages help here (I'm usually frustrated in these discussions when people are always like "Lisp2 Lisp2 blah blah" but that's only part of the story, imo packages play a bigger role), so thanks for pointing that out. I did, however, mention that CL doesn't let you rebind standard names, though.
My point wasn't "Common Lisp (or defmacro) bad, Scheme good", or that you can't write hygienic macros in Common Lisp[1], though it appears that I've come off that way. My point is just that `gensym` alone isn't enough for hygiene (in all cases).
[1]: Another thing that doesn't seem to be understood well enough in these discussions is that hygiene is a property of macros, not macro systems.
Some* argue that hygienic-macros are an unnecessary burden because sometimes you need to capture variable names and this is easy to do with Common Lisp. The situation with Scheme is different because it stores variable and function definitions in the same way (Lisp-1 vs. Lisp-2).
reply