Wednesday, May 11, 2011

The why of macros

Good analysis by Vladimir Sedach:
The entire point of programming is automation. The question that immediately comes to mind after you learn this fact is - why not program a computer to program itself? Macros are a simple mechanism for generating code, in other words, automating programming. [...]

This is also the reason why functional programming languages ignore macros. The people behind them are not interested in programming automation. [Milner] created ML to help automate proofs. The Haskell gang is primarily interested in advancing applied type theory. [...]

Adding macros to ML will have no impact on its usefulness for building theorem provers. You can't make APL or Matlab better languages for working with arrays by adding macros. But as soon as you need to express new domain concepts in a language that does not natively support them, macros become essential to maintaining good, concise code. This IMO is the largest missing piece in most projects based around domain-driven design.

3 comments:

aaron said...

This is spot on, and I've experienced this with Haskell programmers I've run into. As a lazy language, they'll say you have the equivalent of Fexpr's anyway, so why do need macros? Even if you can show an interesting case, they'll hand-wave it away as "syntactic sugar", as if sugar was valueless.

It's spot on. Most Haskell users use it by choice. You have selection bias. A fairer test might be to throw the Haskell programmer into Java for a few weeks, and then ask whether they think macros might be nice to have. I suspect a few will want to be defining macros for handling type declarations and case expressions.

rdm said...

For what it's worth: Ken Iverson's J (which is APL at its heart, though with an almost-but-not-quite-lisp-like parser) has adverbs and conjunctions which are, roughly speaking, macro mechanisms.

Anonymous said...

(set! *rant-mode* #t)

Man, maybe you should stop drinking all that fexpr cool-aid! And you've been dosing quite a bit lately (ever since you discovered Kernel apparently).

Here is the thing about macros:
The macro-expander fundamentally is a part of the compiler. That's all there is to it.

Or restated:
The macro system is your in-language interface to the compiler.

Given a sufficiently powerful macro-system (this is not a theoretical construct, like the infamous "sufficiently smart compiler", but something tangible that any modern Scheme provides) you can modify evaluation semantics (and a lot more) without losing the stable ground that the core language offers you.

Arguments about jitters making fexprs magically stable, efficient, and all that is just fluff.

The only thing you gain by writing things with fexprs is inability to reason consistently about your code.

Aside: Lest you forget the Scheme repl is a just-in-time compiler by itself; it compiles code to core scheme (via the macro-expander) which in-turn is then either meta-circularly evaluated or further compiled to machine code.

Epilogue: You don't need fexprs.

Postscript: Kernel is a theoretical abstraction of languages without semantics. Not to slight Iverson's work, it is a valuable theoretical contribution.

But guess what -- At the end of the day, when the bits hit the metal, you do need semantics in your core language, or else you won't ever be able to interpret it.