6 ms·
As someone who has written a lot of CL and now deals mostly in scheme, why is an unhygienic macro system important? I don't think it matters much, and frankly I
by bjoli 6y ago
As someone who has written a lot of CL and now deals mostly in scheme, why is an unhygienic macro system important? I don't think it matters much, and frankly I think something like vanilla syntax-case isn't hygienic enough. Either I don't want to care about accidentally capturing identifiers (and be very explicit when I do want it - Day like in srgi-72), or I want to have a system where I deal with explicitly (defmacro)
Which I am using matters very little.
- varjag 6y agoIn a lisp-1 like Scheme it's a lot easier to shoot yourself in the foot with unhygienic macros.
- bjoli 6y agoI don't think that matters. Either you write a macro that is correct that won't break because of unwanted intersection with the place of expansion, or your macro is wrong. Edit: barring code-walking macro of course. Once you start using those, the bets are off. Edit2: of course, I don't think overwriting core forms count either. That is poor bedside manners :)
- varjag 6y agoRather than rehash the ancient argument once again, I defer to this post: https://www.xach.com/naggum/articles/3225161536947499@naggum.net.html https://www.xach.com/naggum/articles/3225161536947499@naggum... (KMP was on both Common Lisp and Scheme standard committees; Erik Naggum was an eloquent, if controversial, Lisp expert)
- tines 6y agoCorrect 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.
- ska 6y agoIt's somewhat orthogonal, yes, but a lisp-1 just has more ways to run into the name collision.
- deckard1 6y ago> 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). Bingo. Lisp-2 and GENSYM are attempting to solve the same issue in two different use cases. And, if I may add, GENSYM looks a bit lazy and half-assed next to the scortch-the-earth multiple namespaces of Lisp-2. It's like they blew up Lisp with dynamite and then sat down and said "whatever" when the same problem appears in macros. > The real problem, which Naggum includes, is the lack of GENSYM Guile Scheme has both GENSYM and DEFMACRO (unhygienic). I think quite a few Scheme systems err on the side of practical concerns. But that doesn't erase that fact that GENSYM is an ugly hack to get macros to work.
- varjag 6y ago> Lisp-2 and GENSYM are attempting to solve the same issue in two different use cases. Now this is… post-hoc, Lisp-2 is the original form of LISP and predates the concepts of macros. > And, if I may add, GENSYM looks a bit lazy and half-assed next to the scortch-the-earth multiple namespaces of Lisp-2. I would agree. However in reality we have a system that is half-assed vs a system that nobody likes to use.
- lispm 6y ago> predates the concepts of macros Though LISP macros are quite old, from around 1962...
- bjoli 6y agoMost schemes have a gensym as a part of their lower level macro facilities. It is a part of r6rs (which at least has generate-temporaries, I don't know if gensym is actually standard. All schemes I have used have it).
- aidenn0 6y agoThey both (in this case): 1. are at least a little bit wrong. 2. Never use FLET or LABELS I think we can rule out #2. Function capture is a legitimate problem that does (admittedly rarely) happen in real-world code. The thing that makes writing hygienic macros possible in CL today is the package system. It's disallowed to rebind functions from the CL package, so if your macros only rely on non-exported symbols from your package and symbols in the CL package, then you are free from any hygiene problems that aren't caused by code that is clearly bad locally (that is code that binds internal symbols of other packages; any "::" in use should be immediately flagged by a code review.)
- tines 6y ago> if your macros only rely on non-exported symbols from your package and symbols in the CL package, then you are free from any hygiene problems that aren't caused by code that is clearly bad locally Not quite true, recursive macro invocations that create bindings can step on themselves and force you to use GENSYM, even if your criteria are satisfied.
- aidenn0 6y agoThe "interesting" macro hygiene problems can't be solved with GENSYM. I was assuming it was understood that proper macros should use GENSYM for generated bindings.
- bjoli 6y agoFirst of all, KMP argues that the problem does not exist in a lisp 2. This is obviously wrong, even though the risk is smaller. I would argue that a macro that acts correctly 99% of the time is still wrong. Lisp-2 doesn't solve this. Gensym and packages do. Erik says that gensym and packages solve the same issue that hygiene solves - something I said all along. His main gripe seems to be with the single namespace, which most people dont see as a problem. If you really really need to call your variables `list`, then by all means: use multiple namespaces.
- a0-prw 6y agoI miss Eric Naggum. His rants were absolutely epic. I was terrified of inadvertently offending him on comp.lang.lisp so I always thought extremely carefully before posting anything :)
- bjoli 6y agoI think he was a bully. An intelligent bully, but still a bully. For example: He told me I should get a late abortion (as in suicide) when I as a 14 year old disagreed with him about guaranteed tail recursion. Now, it sounded a bit better in norwegian, but still a pretty horrible thing to say. His spirit lived (lives?) on for a long time, and it made staring common lisp a much shittier experience.
- Jtsummers 6y agoAs an attempt at an answer: Unhygienic macros offer you more "power". With care, or another macro, you can express exactly the things you want with hygienic macros in a system like CL which lacks them, but the reverse isn't true. I cannot think of anything off the top of my head which would be particularly useful with unhygienic macros, but there probably is a use-case. My best guess (and just a guess) might be the SERIES and SCREAMER packages which redefine defun, but I could see it going both ways. A hygienic macro within the SERIES package would know the local definition and use it, but one from without would use cl:defun. With either hygienic or unhygienic macros this can create problems as in either case you may think you're using a particular defun when you're really using the other. NB: My CL macros are written carefully so they'd be roughly equivalent to the hygienic macros of Scheme, but with more boilerplate. At least when it comes to most variables, but I don't take care to ensure the functions used in my macros are not replaced in the local context where they're used.
- bjoli 6y agoI don't believe anything that can be written with unhygienic macros can't be written in a hygienic macro system. Macros written using implicit renaming is written just like CL macros, and with enough libraries you can do something similar with syntax-case. Heck, implementing an unhygienic defmacro over a regular lambda is something like 7 lines of syntax-case. In guile you could even use lambda* to get optional and keyword arguments.
- Jtsummers 6y agoSo, again, I don't think I know for certain why I'd want to do this, but here's an example of different behavior with the two: (defmacro my-not (x) `(not ,x)) (flet ((not (x) x)) (my-not 't)) In a hygienic macro system, that local definition of not will have zero impact on the execution. In an unhygienic macro system, that local definition is used instead of what the macro writer probably intended. That's a stupid, trivial example that would hopefully never be written. My best guess for wanting something like this, per my previous comment, would be a system like SCREAMER or SERIES which could make use of this local redefinition to produce a different-than-standard behavior. How would you write a hygienic macro which would make use of the local redefinition of not without also having to pass the redefinition to the macro?
- tines 6y agoThe 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).
- bjoli 6y agoIf you are arguing defmacro vs. syntax-rules you are indeed correct. The lowest common denominator of the scheme macro world is indeed a pattern substitution one (with hygiene). No procedure application at expansion time allowed. However, all schemes include a lower level macro system that allows you to break hygiene when you want to, most of them by asking explicitly (explicit renaming macro systems being an exception). r6rs, a standard that was disliked by some, standardises a lower level macro system called syntax-case that superficially looks the same as syntax-rules but gives you all the power of unhygienic macro systems (in guile for example, the old unhygienic defmacro is implemented using syntax-case. A very trivial thing to do, I might add). So, to re-hash: syntax-case macros are procedural. They allow for the full power of scheme at expansion time, and allows you to introduce bindings unhygienically using datum->syntax.