My second thought, which I excluded for brevity, was aren't functions and libraries language extensions? I mean technically you could write C without malloc, but it wouldn't be very useful. Regex is a great example of an embedded DSL, but I've never thought "I wish I could write a macro to change it's behavior on the fly in this special case". The last time I looked at this issue I saw that sure, if you don't have generics, it would be helpful to have a language that could write typed functions for you. That makes sense. Changing the parser and compiler behavior before any runtime code executes could be very interesting. There are certainly enough people waxing poetic about the expressiveness of it to make it worth learning.
When the "with" clause ends, the file will automatically be closed. This is more or less bog standard RAII expressed as a new language construct.
In Lisp, this is a feature you could build and provide as a library feature. In fact, we can do it, straight here in an HN comment.
(defgeneric destroy (x)
(:documentation "A destructor for use with the WITH feature".))
(defmacro with ((var = val) &body body)
"Python-style RAII macro. VAR will be
bound to the value of VAL, and upon
exiting scope, will be destroyed with
DESTROY."
(assert (eq = ':=))
(let ((orig (gensym)))
`(let* ((,orig ,val)
(,var ,orig))
(unwind-protect
(progn ,@body)
(destroy ,orig))))
We can register a new destructor easily for open file streams:
(defmethod destroy ((s stream))
(close stream))
(We could add some more error handling, etc. to make it more robust.)
This is now real Lisp code you can actually run, completely integrated into the language, and nicely orthogonal to the existing gamut of features.
This is a concept we can use almost immediately after learning about it, instead of waiting for Python's PEP 6363 to pass with consensus from people who may have different programming goals than you do.
I can see a lot of uses for macros, but in this case C# has a "using" keyword and the designers have put a lot of thought into it and how it is called in a wide variety of situations. Lisp was a language developed in a world when people worked on their own cars, like me. I would have loved Lisp if I had found it earlier, like about the time I rebuilt my VW engine in the 80's.
Many of the language simplifications on more recent C# versions, code generators, Rosylin analysers, expression trees are all features that in Lisp boil down to one thing, macros.
Chapter 3 shows a simple macro, just adding a while loop to the language.
Chapter 9 shows some more complex ones, including a with- macro and a grammar compiler macro.
Chapters 11 and 12 show the development of a Prolog implementation in CL using defmacro to aid in compilation again in Chapter 11.
Chapter 12 shows adding an OO system to the language. Technically not needed with CLOS, but a good demonstration of what can be done with macros.
There are other examples (why I included that search link). Macros let you change the language in ways large and small. Many uses could probably be replaced with functions, though you'd end up having to throw a bunch of quotes about or closures in order to delay processing things. You'd also be delaying that to runtime, which incurs its own penalties compared to macro expansion and compile time (if a compiled CL).
So, as a designer of miniature languages and syntactic extensions, specify it!
A well written function must also abide by some promise or specification—contracts on the inputs and invariants on the outputs. I don't see why new syntactic structures are much different from that.
I think there's undue spookiness around macros among lots of programmers, especially those who haven't sought to solve some problem with them.
- "It's too powerful for the common person."
- "It's way too likely to make code confusing."
- "It's invariably too hard to reason about."
- "It won't work on a team because everybody is going to
make their own weird incompatible language and it's just not scalable."
All of these sentiments are complete bunk, and I argue are against common wisdom around other programming abstractions, like functions or classes.