One of the cleanest examples in the book is the CL-PPCRE library for regular expressions.<p>Without macros, regular expressions are strings at compile time and then at runtime objects are created based on the strings to evaluate them.<p>With macros, regular expressions are expanded at compile time into Common Lisp code that implements the evaluation.<p>There's a similar idea with an ORM library.<p>Without macros, the object-relational mapping is defined at compile time and any dynamic SQL is generated at run time.<p>With macros, the object-relational mapping is used at compile time to expand the query macros into Common Lisp code that includes the actual SQL.<p>Then also remember that the compiler is available at runtime in a lisp, so the distinction only matters when comparing it to non-lisps.
CL-PPCRE actually doesn't really do what most people think it does, cf <a href="https://applied-langua.ge/posts/omrn-compiler.html#orgd52b4ab" rel="nofollow">https://applied-langua.ge/posts/omrn-compiler.html#orgd52b4a...</a> (which instead does).<p>A small tidbit: you don't actually need full syntactic macros to get compile time computation in CL, compiler macros (<a href="https://www.lispworks.com/documentation/HyperSpec/Body/03_bba.htm" rel="nofollow">https://www.lispworks.com/documentation/HyperSpec/Body/03_bb...</a>) suffice.
If your language has macros, you don't need to wait for it to gain async/await (and it was quite a wait even in Python and JS, while Java got its virtual threads very recently); you can just[1] implement a CPS transform as a macro:<p><a href="https://github.com/nim-works/cps/blob/master/docs/cps.svg" rel="nofollow">https://github.com/nim-works/cps/blob/master/docs/cps.svg</a><p>[1] Yeah, I know, this word is extremely overworked in that sentence. Sorry, that's an exaggeration for effect. In reality, it's not that simple, and not always possible: it depends on the language and macro system. It's still more likely to be implementable in library code if there is a macro system, though.
Take C#, if you want a new language feature or syntax added you have to propose it to the language design team, they have to decide it's worth implementing then you have to wait on it being implemented and released and your tooling updated.<p>If I want some new syntax in a LISP, I just write a macro.<p>e.g. the following threading macro in clojure<p>(->> x
foo
bar)<p>Takes x and passes it as the last argument to foo, then the result of foo is passed as the last argument to bar. Alternative you have -> for passing it as the first argument. These are from the standard library, but if they weren't they would be trivial to write yourself.<p>And no strings involved when you write macros in lisp either. It's just lists, your macro is just building a list. Because the syntax is just lists and there's no difference between lists and the syntax.
And that particular threading macro looks a lot like monadic code in Haskell, which looks a bit like Forth, which looks like a shell command with pipes.<p>Macros let a domain programmer (as opposed to the compiler writer) in Lisp implement almost any other programming paradigm as a native language feature.
AFAICT, actually manipulating the AST, which then in turn allows you to implement your own language features, inserting additional debugging info, memoizing a function by just wrapping it in a macro, or optimizations based on domain knowledge.
all the things, if macro system is sufficiently sophisticated (ie., hygienic with ability to attach metadata to syntax object). Racket is one of languages with such system, and they implemented static typing using them.
Generally, it enables type-safe, ahead-of-time compiled code to perform work at compile time that would require runtime execution in many other languages
They can define new syntax and special forms.
[dead]