What are macros used for in Scala? What can be done with a Macro that cannot be done with a function?
I suppose one advantage are:
- The ability to parse the AST at the call point, but that's a fairly rare use case.
- The code doesn't result in an extra function call (modifying the AST directly instead), but modern compilers are pretty good at inlining functions.
Are there any other advantages I'm missing?
I agree with Daenyth, macros have some modest but useful benefits for code-gen.
For arguments sake, consider a spectrum of what play-json could have done to generate code:
A) Two Dumb Runs:
updated strings
use the updated definition in the "real" run.
This is wonderfully simple at first, but clunky and not type-safe
B) Trees and Tasks: read the def in as a string, but use a runtime code-gen library like Treehugger to write code as trees, and an build-tool plugin to add the code-gen task to 'compile'
This gets us halfway type-safety, and sequential compilation by using a plugin offers at least an illusion of a single run.
C) Macros: use an experimental feature to read and write trees at compile time
Macros are fully type-safe, single run, and having everything happen in a single compilation means easily modifying generated code.
For Example
Say I use a code-gen library that adds
def printType
tocase class Record(x: Int)
, givingNow say I want to add my own
def goodbye
to the class as well:Without macros: I could either
1) attempt to modify the output to
but then I encounter
this is a generated file, DO NOT EDIT
printed at the top of the output file, reminding me that the file will be overwritten, so I'll either have to go to the hassle of toggling off code-gen somehow, or2) attempt to modify the input to case class Record(x: Int) { def goodbye = println("bye") } but then the code-gen library probably won't see arbitrary code, so I would have to modify the code-gen library itself.
With macros: if the code-gen library relies on macros (and the library doesn't explicitly dispose of the class body), I can add my new def to the input
and it just works; my def is there, the generated def is there too.