They look the same as normal logging function calls, but entirely avoid the issue of "logging guards."
I'll point you at a good link, but basically if you have a lot of calls to logger.debug, logger.trace, etc, and they involve a lot of string conversions, you pay the penalty for those string conversions all the time even when you're logging at warn or higher. This leads many java developers to do the following:
if (LOGGER.isDebugEnabled()) { LOGGER.debug("last node was: " + node); }
... which makes logging significantly heavier visually.
With a macro you are essentially adding syntax, so you can bake the guards into your log statements.
Logging guards: http://www.kdgregory.com/index.php?page=java.logging
LOGGER.debug("last node was: {}", node);
Where the parameters are a template string and a varargs of objects. The objects are stringified and substituted into the template only once the logging library determines that the message will actually be used.In Scala, you just make the parameter to the call a lazy or call-by-name value, and then there's no need to write an explicit template, the language captures the expression which produces the string and defers its execution until it's needed.
Of course, there are many things macros can do which mechanisms like these can't. The choice in language design is between introducing macros, or introducing enough other mechanisms to make macros unnecessary. Introducing macros seems more parsimonious, but given the utter confusion that macros can be used to wreak, it's not a slam dunk.
And, of course, the Scala designers have introduced all these mechanisms and then added macros anyway, because they're crazy.
Another great example might be Postmodern and CLSQL, both of which include SQL DSLs, much better than concatinating strings or muddling around with ORMs. CL's loop macro basically ads an algol like language to lisp(if you dislike loop, there exist lispier alternatives like iterate).
Macros take care of the case between writing a function and writing an interpreter, and with judicious use, they can help build much cleaner abstractions that give a qualitative improvement to a code base.
There's usually a heavy emphasis in the Clojure community on keeping macros as small as possible – the thinking is that any behavior that can be implemented with ordinary functions should be, and most macros should be lightweight wrappers over these functions. This seems to fit together well with what I've observed: functional programming idioms often circumvent some of the more common legacy uses of macros in practice, relegating macros to a still-essential but less prevalent role.
this is what i was trying to say.
Try to add a static type system to Racket or Clojure. Oh but you don't have the ability to change the runtime system. With macros you can do it. See Typed Racket[2] or Typed Clojure[3].
[1]: http://docs.racket-lang.org/ [2]: https://github.com/plt/racket/tree/master/pkgs/typed-racket-... [3]: https://github.com/clojure/core.typed
Try the Clojure standard libraries. Many central parts of the language like "->>" are macros.
anyway, should clarify: i'm interested in libraries that define and then use lots of macros in order to solve a specific problem. i'd like to know what problem domains are particularly well suited to the sort of language extension that macros provide.
Well no since it's a macro. It's part of the standard library, not a special form part of the language.
Scheme's SRFI-41[0] implements Streams, which are lazily evaluated lists. A few macros are used in order to introduce lazy evaluation to the language. The Streams API wouldn't be very nice at all if no macros were used.
How about something more basic like the `let` macro? It's something you can't live without.
Whenever you need to control the order of evaluation, macros are what you want. Don't be wary of them. They are wonderful.
i would be interested to see links to libraries, particularly clojure libraries, that make heavy but judicious use of macros. it totally makes sense that macros are useful because they capture part of this "code as data" idea that's so central to, esp., lisps. but having more examples of actual problems that they solve would be helpful.