|
|
|
|
|
by dissenter
6569 days ago
|
|
It's obvious, to me at least, that hygienic macros are generally preferable to unhygienic ones. It's also clear to me that macros are an atypical way of thinking about programming. And yet I have a hard time seeing what advantage they yield over code that employs dynamic typing and proper composition of functions. I have seen lots of examples of macros used to do things like square numbers. These are just minimal demonstrations of the technique, so you expect the code to be longer and make less sense. But the same thing appears to happen when the problem is more involved. The code makes less sense, and it's longer than the code I would write for a more traditional solution. I know little about Lisp, but I wonder if Lisp programmers aren't conflating the advantages of macros with the advantages you get simply by building up functions in an intelligent way. Macros are neat, granted, but I've yet to see an instance where they actually increase productivity. If this hypothesis is wrong, I would love to have someone point out why. I have been wary about approaching Lisp precisely because the preoccupation with macros seemed to me to be unjustified (and thus symptomatic of a culture more preoccupied with doing things the computationally interesting way rather than the more powerful way, e.g., using recursion when a for-loop would be shorter and faster). |
|
I would never use squaring things as an example of macros: as you properly point out, this does not illustrate anything that cannot be done at least as easily with functions.
The examples I would trot out are things like let, which adds block scoping to languages that don't have it built in, or things like cond/case/switch. If you have a languge that doesn't have a case-like statement, you can use a macro to make one.
Your current language probably has these things built in, but such macors at least demonstrate a kind of abstraction that cannot be done with functions.
For example, you could build a function that looks like a case statement, but because most languages these days employ eager evaluation, your case function would evaluate all of its outcomes rather than only evaluating the one you want.
To make it work as a pure function, you would have to roll your own thunks by passing anonymous functions in for each case to be evaluated and each outcome. That works, but all that boilerplate becomes tedious.
The macro merely automates that kind of boilerplate for you in places where eager evaluation is harmful.
Time for me to stop, but I'll leave with a suggestion: syntactic abstraction is most useful when there's something unusual about the way you want to evaluate things.