Monday, July 20, 2009

Macros, Preprocessors and DSL development

Along with the recent trend of DSLs becoming more and more popular, we are also seeing a growing trend of programming languages adding preprocessing and macro based features as part of their machinery. Is this a mere coincidence or we are becoming more aware towards Guy Steele's words of wisdom that "a main goal in designing a language should be to plan for growth".

Compile time meta-programming has long been dominated between the 2 extremes of C pre-processors and Lisp macros. In the context of DSL implementation, I have been doing some reading on syntax extension features and meta-programming in various languages. Even I came across this thread in the core-ruby discussion group, where people have been talking about implementing Converge style macros in Ruby. Lisp and Dylan implement macros mainly on top of a language that's syntactically minimal. But nowadays, we are looking at syntax rich languages like Template Haskell and MetaOCaml that implement macros as part of the language.

Converge is, of course a very interesting experiment, where Tratt has implemented Template Haskell like macro capabilities on top of a Python like dynamically typed language. Converge macros are different from Lisp, in the sense that unlike Lisp, they implement macro calls as a special syntax, while macro definitions are regular functions. When the compiler encounters the special syntax in a macro call, it does relevant processing for the quasi-quotations and splice annotations and builds up the resultant AST, which it then merges with the main AST. Thus the AST structure is also abstracted from the user, unlike Ruby and Groovy that allows explicit manipulation of the abstract syntax tree by the user. For details of Converge compile time meta-programming have a look at the Converge site.

Some languages like Nemerle and MetaLua allow dynamic extension of the language grammar through macros. Like Lisp in both of them, macros are not first class citizens, but help implement syntactic extensions in their own unique ways.

So long Haskell has been doing lots of DSL development based on pure embedding using powerful features like monadic interpreters, lazy evaluation and higher order function composition. But macros add yet another level of expressivity in language syntax, not possible through embedding alone. Are we seeing a new and invigorated effort towards implementing syntactic extensions to programming languages ? And does this have any relation to the recent interest and advancements in DSL based development ?


rod said...

I think the increasing focus on language macros/meta-programming and the growth of DSLs are part of the same spectrum.

One way I look at it is also considering Rich Client Platforms (e.g. Eclipse and Netbeans), where the "Inner-platform Effect" is done right (not as an anti-pattern). The development platform and the client platform are one and the same, plus or minus a few plugins/modules.

For example, take an Eclipse-based RCP app written in say Java or Scala, which don't really have meta-programming beyond reflection or being able to invoke their own compiler. Say the RCP app keeps enough of the IDE/compiler infrastructure to be able to compile further code (I mean compiler infrastructure not IDE UI). The app could programmatically generate, compile (in a background thread) and execute further code at runtime, taking into account runtime input. (I guess similar things could also be done outside the RCP environment, since Java and Scala (for example) can invoke their own compilers at runtime).
Getting back the RCP-based app, add in tools like Xtext and Xpand, Acceleo and so on, and you could have runtime code generation and compilation happening in pretty much any language(though easiest with JVM languages). You could even expose part of the runtime code generation to the user if desired, with neat IDE tools.
With this kind of setup, what's the real advantage of (say) clojure or groovy or haskell or whatever, macros/metaprogramming? OK it's part of the language rather than the tools, but if you think of the tools as part of the language, don't the lines begin to blur? Couldn't all the JVM-based languages in such an environment pretty much do similar "meta" things, regardless of inbuilt language meta-ness?

Debasish said...

I agree you can do lots of stuff with tools that allow you to do model based code generation. In fact workbenches like Intentional or Jetbrains MPS allow you to do just that. And the ecosystem does continue to expand. IMHO the language is your best tool and something which I can get from my language is the one that comes with minimum payload. If Java supports DI today, will u be using Spring for doing the same ?