About Adam Warski

Adam is one of the co-founders of SoftwareMill, a company specialising in delivering customised software solutions. He is also involved in open-source projects, as a founder, lead developer or contributor to: Hibernate Envers, a Hibernate core module, which provides entity versioning/auditing capabilities; ElasticMQ, an SQS-compatible messaging server written in Scala; Veripacks, a tool to specify and verify inter-package dependencies, and others.

Starting with Scala Macros: a short tutorial

Using some time during the weekend, I decided to finally explore one the new features in the coming Scala 2.10, macros.

Macros are also written in Scala so in essence a macro is a piece of Scala code, executed at compile-time, which manipulates and modifies the AST of a Scala program.

To do something useful, I wanted to implement a simple macro for debugging; I suppose I’m not alone in using println-debugging, that is debugging by inserting statements like:
 
 

println('After register; user = ' + user + ', userCount = ' + userCount)

running a test, and checking what the output is. Writing the variable name before the variable is tedious, so I wanted to write a macro which would do that for me; that is:

debug('After register', user, userCount)

should have the same effect as the first snippet (it should generate code similar to the one above).

Let’s see step-by-step how to implement such a macro. There’s a good getting started guide on the scala macros page, which I used. All code explained below is available on GitHub, in the scala-macro-debug project.

1. Project setup

To experiment comfortably we’ll need to setup a simple project first. We will need at least two subprojects: one for macros, and one for testing the macros. That is because the macros must be compiled separately and before and code that uses them (as they influence the compilation process).

Moreover, the macro subproject needs to have a dependency on scala-compiler, to be able to access the reflection and AST classes.

A simple SBT build file could look like this: Build.scala.

2. Hello World!

“Hello World!” is always a great starting point. So my first step was to write a macro, which would expand hello() to println('Hello World!') at compile-time.

In the macros subproject, we have to create a new object, which defines hello() and the macro:

package com.softwaremill.debug

import language.experimental.macros

import reflect.macros.Context

object DebugMacros {
  def hello(): Unit = macro hello_impl

  def hello_impl(c: Context)(): c.Expr[Unit] = {
    // TODO
  }
}

There are a couple of important things here:

  1. we have to import language.experimental.macros, to enable the macros feature in the given source file. Otherwise we’ll get compilation errors reminding us about the import.
  2. the definition of hello() uses the macro keyword, followed by a method which implements the macro
  3. the macro implementation has two parameter lists: the first is the context (you can think about it as a compilation context), the second mirrors the parameter list of our method – here it’s empty. Finally, the return type must also match – however in the method we have a return type unit, in the macro we return an expression (which wraps a piece of an AST) of type unit.

Now to the implementation, which is pretty short:

def hello_impl(c: Context)(): c.Expr[Unit] = {
  import c.universe._
  reify { println('Hello World!') }
}

Going line by line:

  1. first we import the “universe”, which gives convenient access to AST classes. Note that the return type is c.Expr – so it’s a path-dependent type, taken from the context. You’ll see that import in every macro.
  2. as we want to generate code which prints “Hello World!”, we need to create an AST for it. Instead of constructing it manually (which is possible, but doesn’t look too nice), Scala provides a reify method (reify is also a macro – a macro used when compiling macros), which turns the given code into an Expr[T] (expressions wrap an AST and its type). As println has type unit, the reified expression has type Expr[Unit], and we can just return it.

Usage is pretty simple. In the testing subproject, write the following:

object DebugExample extends App {
  import DebugMacros._
  hello()
}

and run the code (e.g. with the run command in SBT shell).

3. Printing out a parameter

Printing Hello World is nice, but it’s even nicer to print a parameter. The second macro will do just that: it will transform printparam(anything) into println(anything). Not very useful, and pretty similar to what we’ve seen, with two crucial differences:

def printparam(param: Any): Unit = macro printparam_impl

def printparam_impl(c: Context)(param: c.Expr[Any]): c.Expr[Unit] = {
  import c.universe._
  reify { println(param.splice) }
}

The first difference is that the method accepts a parameter param: Any. In the macro implementation, we have to mirror that – but same as with the return type, instead of Any, we accept an Expr[Any], as during compile-time we operate on ASTs.

The second difference is the usage of splice. It is a special method of Expr, which can only be used inside a reify call, and does kind of the opposite of reify: it embeds the given expression into the code that is being reified. Here, we have param which is an Expr (that is, tree + type), and we want to put that tree as a child of println; we want the value that is represented by param to be passed to println, not the AST. splice called on an Expr[T] returns a T, so the reified code type-checks.

4. Single-variable debug

Let’s now get to our debug method. First maybe let’s implement a single-variable debug, that is debug(x) should be transformed into something like println('x = ' + x).

Here’s the macro:

def debug(param: Any): Unit = macro debug_impl

def debug_impl(c: Context)(param: c.Expr[Any]): c.Expr[Unit] = {
  import c.universe._
  val paramRep = show(param.tree)
  val paramRepTree = Literal(Constant(paramRep))
  val paramRepExpr = c.Expr[String](paramRepTree)
  reify { println(paramRepExpr.splice + ' = ' + param.splice) }
}

The new thing is of course generating the prefix. To do that, we first turn the parameter’s tree into a String. The built-in method show does exactly that. A little note here; as we are turning an AST into a String, the output may look a bit different than in the original code. For vals declared inside a method, it will return simply the val name. For class fields, you’ll see something like DebugExample.this.myField. For expressions, e.g. left + right, you’ll see left.+(right). Not perfect, but readable enough I think.

Secondly, we need to create a tree (by hand this time) representing a constant String. Here you just have to know what to construct, e.g. by inspecting trees created by reification (or reading Scala compiler’s source code).

Finally, we turn that simple tree into an expression of type String, and splice it inside the println. Running for example such code:

object DebugExample extends App {
  import DebugMacros._

  val y = 10

  def test() {
    val p = 11
    debug1(p)
    debug1(p + y)
  }

  test()
}

outputs:

p = 11
p.+(DebugExample.this.y) = 21

5. Final product

Implementing the full debug macro, as described above, introduces only one new concept. The full source is a bit long, so you can view it on GitHub.

In the macro implementation we first generate a tree (AST) for each parameter – which represents either printing a constant, or an expression. Then we interleave the trees with separators (', ') for easier reading.

Finally, we have to turn the list of trees into an expression. To do that, we create a Block. A block takes a list of statements that should be executed, and an expression which is a result of the whole block. In our case the result is of course ().

And now we can happily debug! For example, writing:

debug('After register', user, userCount)

will print, when executed:

AfterRegister, user = User(x, y), userCount = 1029

Summing up

That’s quite a long post, glad somebody made it that far. Anyway, macros look really interesting, and it’s pretty simple to start writing macros on your own. You can find a simple SBT project plus the code discussed here on GitHub (scala-macro-debug project). And I suppose soon we’ll see an outcrop of macro-leveraging projects. Already there are some, for example Expecty or Macrocosm.
 

Reference: Starting with Scala Macros: a short tutorial from our JCG partner Adam Warski at the Blog of Adam Warski blog.

Related Whitepaper:

Java Essential Training

Author David Gassner explores Java SE (Standard Edition), the language used to build mobile apps for Android devices, enterprise server applications, and more!

The course demonstrates how to install both Java and the Eclipse IDE and dives into the particulars of programming. The course also explains the fundamentals of Java, from creating simple variables, assigning values, and declaring methods to working with strings, arrays, and subclasses; reading and writing to text files; and implementing object oriented programming concepts. Exercise files are included with the course.

Get it Now!  

Leave a Reply


× seven = 42



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books