

Join the discussion
Write your take first — we'll ask for email only when you're ready to publish.
- Hacker News
- If macros can implement arbitrary language features then how come elisp has never built a type system?by KeenanKeenan
- by drob518
- by wk_end
- I would expect you can.
lexilambda created Typed Racket on top of Racket. https://github.com/racket/typed-racket
by BeetleB - > If macros can implement arbitrary language features
Not really your question--others have provided examples of type systems as macros--but they can't, they can only do local rewrites. You couldn't, for example, express mutation or non-local control flow if you didn't have them already; On the expressive power of programming languages [0] is rather theoretical but does set out the boundary of what macros can and can't do. That they're local means everyone has to buy into your type system if you want it to be a macro, though.
[0] https://www.sciencedirect.com/science/article/pii/0167642391... though https://www.youtube.com/watch?v=43XaZEn2aLc might be a useful walkthrough of the ideas being presented
- I always thought it odd that Penrose gets dropped from the Escher conversations.
- Can you elaborate? Roger Penrose and his father Lionel did independently discover and popularize the Penrose Stairs and Penrose Triangle but Oscar Reutersvärd had created both years earlier. What do you think should be said about Penrose?by mangodrunk
- Why is most AI work done in Lisp? There are many reasons, most of which are somewhat technical, but one of the best is quite simple: Lisp is crisp. Or as Marilyn Monroe said in The Seven-Year Itch, "I think it's just elegant!"
This claim didn't age well.
by bossyTeacher - Haha, this is very true. Symbolic AI is dead for the most part, but I feel like it was an issue of labor scale. There weren't enough code fluent humans to embed all the knowledge. I do think symbolic AI will see a resurgence though. I could see it being a part of enrichment to current models. I don't think it's the case that we'll see diminishing returns THEN reach for symbolic AI. I think that adding a symbolic layer could greatly enhance their effectiveness today and make the models cheaper (by being performant with fewer params and therefore cheaper to run and serve). You already kind of see clues of this as creating graphs / ontologies as part of a RAG or retrieval phase are proven to improve performance of these AI models in specific domains.
At the same time, back then the thought was that symbolic AI was the answer, and for sure Hofstadter's understanding of intelligence, and the emergence of consciousness from matter, had everything to do with symbols. He's talked about this, very interesting, although this is not a 'feel good' artifact from the Hofstadter canon lol. https://www.youtube.com/watch?v=R6e08RnJyxo&t=227s
by chiply - Remember the golden rule of Lisp macros: don't write a macro.by kimi
- Why?
- Meh, that seems to be a bit of a clojure thing. In the Racket world, with hygienic macros and phase separation, they'll routinely write macros returning macros, etc.by hencq
- Ok, common-lisp newbie here.
Could you explain why Paul Graham's book "On Lisp" is wrong? Or misguided? Or misunderstood by newbies like me? 30 years of c++ with a lot of dealing with templates and I'm not feeling the mandatory function purity here.
by downut - I agree! A Lisp coder should define a new macro only rarely. Junior coders should avoid doing it altogether.
Macros make Lisp code hard to read. Every time I see a call to define-minor-mode, a groan a little: that macro saves about 5 tokens (relative to just writing out code involving only functions everyone knows) at the cost of making me bounce back and forth at least 3 times between (a buffer showing) define-minor-mode's doc string and the call.
by hollerith - Author here. I think in general this is sound advice, but in Emacs it is an incredibly convenient utility. I use them all the time. Mostly simple things like wrapping function definitions, or even other macros that wrap function definitions. Most of my lisp experience is in Emacs where the config layer is essentially exposed through an elisp API. In that context, I find myself using macros a lot. But if I was building some software from scratch, like say a data processing system, I can't immediately think of scenarios where I would define macros.
Worth mentioning in Elisp that even if you don't define your own macros, you use them more often than you would think. defun, unless, def-custom, etc (more examples in the blog post) are all macros. The fact they look and feel like non-macros like special forms and built in functions implemented in C is part of what makes elisp so cool to me. There are plenty of homoiconic languages, but you rarely feel the distinction between program and data in elisp, and macro-supporting lisps in general.
by chiply - That is badly generalized proverb. Clojure has unhygienic macros, no expander extension points, and no error-message layer, so "don't" is a rational default there. Yet, for example Racket writes macros that write macros as a matter of course. And that's because the language has the infrastructure: hygiene by default, phase separation, `syntax-parse` with grammar-quality error messages, a module system that actually knows about compile-time dependencies.
But even with some limitations, there are cases in Clojure where macros do achieve things otherwise unattainable. One practical example is multi-host code generation. You can have a Clojurescript macro that parses and analyzes some javascript lib, doing that on JVM side, while emmiting code generated to run in Javascript. Hyperfiddle/Electric is the best example of that kind of macro ingenuity.
Or take core.async's `go`. It takes an arbitrary body, analyzes it into an AST, and rewrites it into a state machine so that <! and >! can park and resume. On the JVM it lets you avoid blocking threads. In Clojurescript it is the only way to have the model at all, because JS has no threads to block.
Nothing about that is expressible as a function. The transformation needs the entire body as data. Same family: core.match compiles a pattern matrix into a decision tree. And there are many more example use cases for macros.
"Don't write macros" rule has the second part: "unless you truly have no choice."
by iLemming - The Computational Beauty of Nature it's similar and it has the code of the book there:
Some tools might crash X under CWM for OpenBSD, switch to FVWM or use some tool like mimalloc.
On Lisp, the book basically builds integers based on conses, kinda like Peano Axioms.
by anthk - > To anticipate a common question: why couldn't my-unless be a function? Function arguments are evaluated eagerly, before the function ever sees them.
Interesting, so if you’re using a lazy language then you don’t need a macro here and could write my-unless as a function.
by meken - Unless laziness is pervasive (I guess, no knowledge about GHC internals), you basically have an FEXPR (https://en.wikipedia.org/wiki/Fexpr) which was replaced by macros for good reasons.
- You could do that as well with lisp by passing around lambdas to functions but that adds unnecessary syntax.by phyzix5761
- Or you could wrap arguments in lambdas (anonymous functions) for eagerly-evaluted languages. Lisp unfortunately has a bulky syntax for lambdas, compared to something like Smalltalk. Of course, you can fix this with reader macros in Common Lisp (but no one does). Clojure has a shorter syntax I believe as well: #().by floxy
- by wk_end
- Yes, the D language has that as a feature in function arguments!
https://dlang.org/articles/lazy-evaluation.html
It makes it hard to know when things run. In Lisp you also have that problem everywhere, of course.
As the post shows this allows you to do stuff that looks like extending the syntax of the language.
I can’t decide if I love it or hate it!
by brabel