Whew, the SQL there is a really good example of why SQL irks me so much. The lack of composition is so real; you Just Have To Know how to write what you want to put into each slot.
I maintain that Common Lisp is the gold standard of DX for macros, but it works only because Common Lisp exists at a very narrow conjunction of tradeoffs. In particular, image-based development is a rarity nowadays […] it’s the thing that makes it possible to have Turing-complete macros that are defined in the same place as the code, without needing to involve a build system.
Uhhh wait what? I don’t see how these two features are connected at all. I wish the post would elaborate on that. Feels like a non-sequitur.
Among people who don’t use CL, the CL macro system is considered fairly clunky and hard to use effectively because it’s very easy to do accidental symbol capture if you don’t use gensym in the right places. Early on Scheme developed a very complex solution to this with their hygenic macro system, and most CLers scoffed at it because they said the cost was too high, and accidental symbol capture “wasn’t all that bad”. But the auto-gensym feature introduced by Clojure along with protections against non-gensym identifiers basically solves the same problem with a fraction of the conceptual overhead and lets you keep using the “obvious” defmacro style of CL without the danger.
Uhhh wait what? I don’t see how these two features are connected at all. I wish the post would elaborate on that. Feels like a non-sequitur.
Yes, other CLs (and me) agree. The author probably mean the interactive flow where you feed code to the running process instead. Similar to one does in Clojure, and Clojure doesn’t have any kind of images.
Ref: https://twitter.com/stylewarning/status/1665754390639304707
hard to use effectively because it’s very easy to do accidental symbol capture if you don’t use gensym in the right places.
I can see how it is very easy to do accidental symbol capture in Scheme, which has a single namespace for symbols. Ignoring the fact that CL has different namespaces for variables, functions, classes, etc, symbols in CL are namespaced under a package. This results in accidental symbol capture being less frequent in practice. That said CL also gives you the tools to avoid the problem, as you mention.
If it was easy to do in CL, one would frequently run into bugs relating accidental symbol capture, do you know of any? To be clear, I have found macros were that should have used gensym but didn’t. I have not found bugs in the call-sites of said macros.
CLers scoffed at it because they said the cost was too high, and accidental symbol capture “wasn’t all that bad”.
The use of quotes without a reference looks like a strawman. Do you have a source for that? What I’ve seen people in the CL community argue is that hygienic macros are more complex and you can solve the problem with other means (gensym).
hygenic macros have other benefits (like improved error reporting when using the … pattern).
But the auto-gensym feature introduced by Clojure along with protections against non-gensym identifiers basically solves the same problem with a fraction of the conceptual overhead and lets you keep using the “obvious” defmacro style of CL without the danger
I wasn’t aware of that feature, seems like a reader macro to generate unique symbols. Looks similar to CL’s uninterned symbols, which is what gensym is built upon. CL has syntax for uninterned symbols, #:. The name of the symbol gensym returns is unimportant, as even if two gensymed symbols have the same name they will still be treated as different symbols.
I think he means something subtly different by ‘image-based development’ than most folks do. The common meaning of an image is persistent mutable runtime state, like a Smalltalk image. Common Lisp doesn’t have to have that, and I don’t believe that is how it is commonly used.
I think what he means is that the entire runtime system is available at read macros run, and at compile time, when macros run. But I could be wrong.
In particular, image-based development is a rarity nowadays, a Galápagos island feature that is undesirable in many contexts, but it’s the thing that makes it possible to have Turing-complete macros that are defined in the same place as the code, without needing to involve a build system.
Racket has “Turing-complete macros that are defined in the same place as the code”, and it doesn’t use the image model. I’m pretty sure most (if not all) Schemes let you do that too. Or am I missing something?
Whew, the SQL there is a really good example of why SQL irks me so much. The lack of composition is so real; you Just Have To Know how to write what you want to put into each slot.
Uhhh wait what? I don’t see how these two features are connected at all. I wish the post would elaborate on that. Feels like a non-sequitur.
Among people who don’t use CL, the CL macro system is considered fairly clunky and hard to use effectively because it’s very easy to do accidental symbol capture if you don’t use
gensym
in the right places. Early on Scheme developed a very complex solution to this with their hygenic macro system, and most CLers scoffed at it because they said the cost was too high, and accidental symbol capture “wasn’t all that bad”. But the auto-gensym feature introduced by Clojure along with protections against non-gensym identifiers basically solves the same problem with a fraction of the conceptual overhead and lets you keep using the “obvious” defmacro style of CL without the danger.Yes, other CLs (and me) agree. The author probably mean the interactive flow where you feed code to the running process instead. Similar to one does in Clojure, and Clojure doesn’t have any kind of images. Ref: https://twitter.com/stylewarning/status/1665754390639304707
I can see how it is very easy to do accidental symbol capture in Scheme, which has a single namespace for symbols. Ignoring the fact that CL has different namespaces for variables, functions, classes, etc, symbols in CL are namespaced under a package. This results in accidental symbol capture being less frequent in practice. That said CL also gives you the tools to avoid the problem, as you mention.
If it was easy to do in CL, one would frequently run into bugs relating accidental symbol capture, do you know of any? To be clear, I have found macros were that should have used gensym but didn’t. I have not found bugs in the call-sites of said macros.
The use of quotes without a reference looks like a strawman. Do you have a source for that? What I’ve seen people in the CL community argue is that hygienic macros are more complex and you can solve the problem with other means (gensym).
hygenic macros have other benefits (like improved error reporting when using the … pattern).
I wasn’t aware of that feature, seems like a reader macro to generate unique symbols. Looks similar to CL’s uninterned symbols, which is what gensym is built upon. CL has syntax for uninterned symbols,
#:
. The name of the symbolgensym
returns is unimportant, as even if two gensymed symbols have the same name they will still be treated as different symbols.I think he means something subtly different by ‘image-based development’ than most folks do. The common meaning of an image is persistent mutable runtime state, like a Smalltalk image. Common Lisp doesn’t have to have that, and I don’t believe that is how it is commonly used.
I think what he means is that the entire runtime system is available at read macros run, and at compile time, when macros run. But I could be wrong.
Racket has “Turing-complete macros that are defined in the same place as the code”, and it doesn’t use the image model. I’m pretty sure most (if not all) Schemes let you do that too. Or am I missing something?
That is probably the best explanation for this that I’ve seen so far, well done! :)