4 ms·
i don't fear macros, but i am wary of them: when there are only a few macros, it seems as though replacing them with functions makes more sense, even if the sy
by auvrw 13y ago
i don't fear macros, but i am wary of them: when there are only a few macros, it seems as though replacing them with functions makes more sense, even if the syntax is slightly messier. when there are so many macros that you've essentially made an entirely new language, writing a REPL feels like a cleaner separation, even if it is more work.
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.
- leephillips 13y ago"particularly clojure libraries, that make heavy but judicious use of macros" Try the Clojure standard libraries. Many central parts of the language like "->>" are macros.
- auvrw 13y agooh, yes, indeed, the ->> macro is slick. reminds me of the bind operator in haskell. i don't know why i haven't seen something like it in other lisps. but as you said, that's part of the language. 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.
- masklinn 13y ago> but as you said, that's part of the language. Well no since it's a macro. It's part of the standard library, not a special form part of the language.
- TeMPOraL 13y agoThe point of macros is to blur the distinction between what's "built-in" and what's "user-defined" to the point of irrelevancy.
- greghendershott 13y agoFor example, threading macros in Racket: https://github.com/greghendershott/rackjure/blob/master/rackjure/threading.rkt https://github.com/greghendershott/rackjure/blob/master/rack...
- Psyonic 13y agoA good example, clojure logging. https://github.com/clojure/tools.logging https://github.com/clojure/tools.logging 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 http://www.kdgregory.com/index.php?page=java.logging
- deleted 13y ago[deleted]
- twic 13y agoAlthough, for what it's worth, Java programmers solved this quite some time ago by allowing crude lazy evaluation of the string. Logging calls actually look like: 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.
- mkremins 13y agocore.async [1] is basically the canonical example of a Clojure library that relies on macros to do its job, although surprisingly few macros actually need to be exposed in the public API. Most notable is the go macro, which performs deep-walking code transformation on its body, permitting clients to write async code in a straightforward, boilerplate-free way. 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. [1] https://github.com/clojure/core.async https://github.com/clojure/core.async
- auvrw 13y ago> the thinking is that any behavior that can be implemented with ordinary functions should be this is what i was trying to say.
- pavelludiq 13y agoMy favorite example would have to be CLOS the Common Lisp object system. Although it's a bit hairy in certain places, it essentially transforms a procedural/functional language into on of the most powerful object oriented languages around. Macros play a small, but crucial part of it, subverting the rules of lisp in order to add another dimension to the language. 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.
- deleted 13y ago[deleted]
- davexunit 13y agoI don't know Clojure, but here's a couple of good macro examples in Scheme. 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. [0] http://srfi.schemers.org/srfi-41/srfi-41.html http://srfi.schemers.org/srfi-41/srfi-41.html
- stchang 13y agoAll of Racket's `#lang` family of languages are implemented with macros on top of core Racket. For example, Scribble, mentioned in another comment, is the documentation language used to author the article, and the Racket docs[1]). Or Slideshow, Racket's programmatic Powerpoint alternative. 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/ http://docs.racket-lang.org/ [2]: https://github.com/plt/racket/tree/master/pkgs/typed-racket-pkgs/typed-racket-lib https://github.com/plt/racket/tree/master/pkgs/typed-racket-... [3]: https://github.com/clojure/core.typed https://github.com/clojure/core.typed