9 ms·
I find this argument pretty unconvincing too. >For all practical purposes, you can't add that to Java. You'll always have to wrap up your transactions in boile
by blueblob 11y ago
I find this argument pretty unconvincing too.
>For all practical purposes, you can't add that to Java. You'll always have to wrap up your transactions in boilerplate.
Aren't you wrapping the lisp code in boilerplate when doing the macro too? This appears to be the same as your other example. Java has lambda expressions (since Java 8) that could do this.
If you are wrapping it in a macro, how is it different than wrapping it in a method? In Java 8, with lambda expressions, you can write code to wrap your transaction example to get something exactly equivalent to
>transaction(stuff, commit-stuff, rollback-stuff)
and you would be keeping the definition in the implementation. I also seem to be missing why it's important to be keeping the definition and implementation together.
- the_af 11y agoAgreed, I'm still unconvinced for the same reasons. In Scala or Haskell (or any language which supports first-class functions and lazy evaluation, I guess) the "transaction" example is easily done.
- zck 11y agoSo there's some boilerplate that needs to happen. With macros, you write the macro to insert the boilerplate, and then you never think about it again. You don't write it, you don't read it, it's not in the way. Without macros, you have to write the boilerplate every time you write the code. You have to read the boilerplate every time you read the code. Here's the version with macros: (transaction (do-stuff) (do-stuff-if-commit-successful) (do-stuff-if-rollback)) Here's the version without macros: (transaction (lambda () (do-stuff)) (lambda () (do-stuff-if-commit-successful)) (lambda () (do-stuff-if-rollback))) Which do you find more readable? Notice how there's no boilerplate in the macro version. > This appears to be the same as your other example. Java has lambda expressions (since Java 8) that could do this. What are the odds that Java 8 has provided everything you could want in out of Java? Macros let you add things in a better way than you could otherwise get. Look back to my prior example of Java 5's expanded for. You see how useful that was? If Java had had macros, people wouldn't have had to suffer through nine years without the improved for loop.
- the_af 11y ago@ zck: > What are the odds that Java 8 has provided everything you could want in out of Java? Macros let you add things in a better way than you could otherwise get. Look back to my prior example of Java 5's expanded for. Again, please understand we non-Lispers find this argument utterly unconvincing :) Java in particular is a terrible example: it's a language that for many years remained in the dark ages, and now it's finally getting modern features retrofitted into it, while at the same time attempting to keep some sort of backwards compatibility, and the whole process is very painful. Let's all agree to stop talking about Java. Let's assume we all agree working in a language that until very recently didn't have lambdas is painful. And that requires a horrific amount of boilerplate. Let me re-throw your question back at you: what exactly can Lisp do, which has practical implications, that a modern language with lambdas, closures and lazy evaluation cannot accomplish in elegant ways? If you mention Java again, you lose :P
- zck 11y agoYou seem to be concerned by the use of "Java". It's a fine example, and one I think I've explained why very well. It also seems petulant to declare Java off-limits. But let me try again. For _any given language_, there are things you may want that the language doesn't provide. Macros let you do that in an elegant way. The example I gave above -- based off LanceH's -- of transactions is a way where the macro-based solution is more elegant than non-macro solutions. Here's another example. Arc, like any language, has a built-in way of setting variables. It's called `assign`, and can be used as follows: arc> (assign a 3) 3 arc> a 3 But no one uses it. Instead, people use =. = is a macro that's provided with the language. Because it's a macro, that means that if it wasn't provided, you could write it yourself.^1 What's the benefit of = ? It lets you set values of more than just variables: arc> (= my-table (table)) #hash() arc> (my-table 'key) ;;look up the value nil arc> (= (my-table 'key) 'value) value arc> (my-table 'key) value Note that in the second prompt, we're attempting to look up the value of 'key in the hashtable my-table, and we see that there isn't one there. We then set it in the third prompt, and look it up again in the final. This works on the concept of "places". The "place" we try to set in `(= (my-table 'key) 'value)` is the association of 'key inside my-table. Why is this beneficial? If you know how to get a value out of a data structure, you can now set it. This code is extremely clean and understandable compared to a non-macro version. It exhibits the principle of least surprise, and it's obvious how to set other data structures in an elegant way. You can't do this without macros. [1] If your objection here is "but it comes with the language", you're missing the point.