4 ms·
So you think of a feature you'd like in your language. Let's consider the process you'd have to go through to use that feature. In most languages, you have to
by zck 11y ago
So you think of a feature you'd like in your language. Let's consider the process you'd have to go through to use that feature.
In most languages, you have to write the maintainers about the feature. You then have to convince them that the idea is good -- and this is by no means guaranteed; if they think your idea is bad, you're out of luck, and can never use the feature you'd like. Then someone has to implement it. Then you have to wait for a release containing the feature. Then you have to test your code with the new version. Then you have to upgrade all your deploys, development environments, and testing machines to the new version. Then you can use the new feature.
In a Lisp with macros, you have to implement the feature. Then you can use it.
This is why macros are useful. You get to modify the language you're using, but still cut out the entire loop of the maintainers of that language.
- the_af 11y agoYes, I understand that argument, but I still find it unconvincing. Some features you just can't implement with macros, you need a change in the "substrate" (see kazinator's answer below). For the rest, I simply don't see how they are language-level features. They are just things you need for your project, in which case, why can't you simply implement them as a library? Even if you ignore the above, there's probably a good reason why the language designers don't want to approve your language-level feature. Yes, sometimes it's simply red tape or politics, but it can also be that you -- the applications programmer -- simply aren't well-versed in language design and can't think past your particular use case :) This wouldn't mean the feature is worthless (after all, you need it!) but maybe it's not meant to be a language-level feature, but instead... a library function, which you can write in most general purpose languages.
- zck 11y agoBecause macros let you control code in a way libraries don't^1. Java now has this way to iterate over a collection of things: for(String exclamation: exclamations) { System.out.println("I yell " + exclamation + " at you"); } But this was only added in 2004!^2 So for almost 10 years, you had to manually iterate over stuff. How would you implement this as a library? Well, you could write a function that lets you write: iterate_over_collection(exclamations, function(collection) { System.out.println("I yell " + exclamation + " at you"); } But this is much uglier than the prior code. Also, it might not act the same. For example: int highest = ages[0]; for(int age: ages) { highest = Math.max(highest, age); } Would this work in a lambda? Well, if the language you're using has real closures, yes -- but does it? Do you know offhand? With a macro, your code will work. > Even if you ignore the above, there's probably a good reason why the language designers don't want to approve your language-level feature. ... it can also be that you -- the applications programmer -- simply aren't well-versed in language design and can't think past your particular use case :) This wouldn't mean the feature is worthless (after all, you need it!) That seems like evidence for my point -- macros let you build the language up for your own use case, not anyone else's. Without macros, your choices are "either everyone can use it, or no one can use it". Macros let you have a choice of "well, I can use it, even if no one else wants it, if I find it useful." Macros can be viewed as libraries that act on the language itself. There isn't a difference between language-level features and "library functions" in a language with macros. [1] Without getting into a Turing Tarpit. We're talking about using things in easy ways, not what is technically possible but ugly and kludgy. [2] It was released in Java 5.0: http://www.java-tips.org/java-se-tips/java.lang/the-enhanced-for-loop.html http://www.java-tips.org/java-se-tips/java.lang/the-enhanced...
- the_af 11y agoAgreed about your Java example. However, for the purpose of this discussion, let's assume we're talking about modern, well-designed languages without ugly kludges and with access to nice features such as lazy evaluation and real closures. > Macros can be viewed as libraries that act on the language itself. There isn't a difference between language-level features and "library functions" in a language with macros. I simply don't see why this is such a big deal. I need to see a real-world example (which, understandably, might be difficult to explain in a HN thread) of something that can be achieved with Lisp that is not reasonably achievable in elegant ways in other, non-Lisp modern languages. Again, let's assume we both understand the Turing Tarpit. By the way, I don't want to sound dense. I understand some features you only "get" when you use them. What little I've seen of Lisp (Racket, actually) seemed very interesting! It's just that I can't get that enlightened moment where I see why Lisp macros are that important in the real world. This is important to me because macros are one of the key features Lispers use to try to convince other programmers Lisp is awesome. And I can see they are interesting and useful; I just fail to see why they are a such big deal that they set Lisp apart and that, for example, Paul Graham would call Lisp his "secret sauce".
- charlieflowers 11y agoMany have said that the real benefit is that you wind up turning Lisp into the language that is perfect for your domain. When you start your project, you don't know enough to design the perfect language for your domain. You start coding in Lisp, and you begin to uncover patterns that express your domain. Eventually, you find your way towards building a small set of macros that beautifully, expressively capture your domain. Look for where Paul Graham talks about "bottom-up" programming versus "top-down" programming, and you'll find what he has to say about this. He says you do both in Lisp. Bottom-up is "changing the language to suit your problem." http://www.paulgraham.com/progbot.html http://www.paulgraham.com/progbot.html
- LanceH 11y agoOne that helped some Java friends understand is passing blocks of code, but still having it look like just writing code. Imagine instead of try/catch/finally, a transaction/commit/rollback in Java: transaction { // everything in here is in one transaction } commit { // do stuff if the commit is successful } rollback { // do stuff if we rollback } All the try's and catch's can be stuff into the macro. It can be made to nest transactions within transactions. For all practical purposes, you can't add that to Java. You'll always have to wrap up your transactions in boilerplate. The Lisp equivalent of what I want, the resulting code would look like: (transaction (do-stuff) (do-stuff-if-commit-successful) (do-stuff-if-rollback)) There are languages where I could define three blocks of code and pass them: transaction(stuff, commit-stuff, rollback-stuff) But that separates their definitions from their implementations. How could you write the Lisp transaction expression in another language so that it looks like it's part of the language? (serious question, CL is the only language I use capable of being that close) Maybe I could torture Ruby to come close, but it would be far more difficult than the macro I had to write for Lisp.