Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

Unhygienic macros aren't a C-ism: hygiene was an innovation (I think originally in Scheme). Ordinary Common Lisp macros are unhygienic.


sort by: page size:

Common Lisp does have macros, but not hygienic macros.

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.

I wasn't sure I could find it again, but here it is!

Embedding Hygiene-Compatible Macros in an Unhygienic Macro System: https://www.jucs.org/jucs_16_2/embedding_hygiene_compatible_...

    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)

Common Lisp do have optional typing though, and hygienic macros is a controversial subject. Dialect scoping is cool though

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.

Apropos, I've written a Lisp->JS compiler a while ago; you might find some of the code useful: http://github.com/manuel/cyberlisp/


Hygienic macros are a bigger issue in 1-lisps dude to high density of namespace collisions.

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.


In a lisp-1 like Scheme it's a lot easier to shoot yourself in the foot with unhygienic macros.

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.


R5RS hygienic macros qualify as LISP goodness, but a lot of Scheme implementations also implement defmacro (like CL) and/or syntax-case.

Use of macros makes the code reflect the programmer's thinking about the problem domain. Whether this is readable or not depends on the programmer.


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


It supports macros. It's a lisp-1 and they're not hygienic... but you can hack away with them.

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.


The debate over hygienic-macros is not as straight-forward: https://groups.google.com/d/topic/comp.lang.lisp/dcX15VC5BdM...

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

* http://p-cos.net/lisp/guide.html

next

Legal | privacy