Over a million developers have joined DZone.

Debugging Scala Macros

· Performance Zone

Download Forrester’s “Vendor Landscape, Application Performance Management” report that examines the evolving role of APM as a key driver of customer satisfaction and business success, brought to you in partnership with BMC.

println is the golden hammer of debugging, but when it comes to debugging your Scala Macros, I find it useful to be able to set a breakpoint at the point of the macro expansion. In this post, I’ll show you how to construct a simple macro and how to debug it in IntelliJ IDEA.

Screencast

Macros from Cake Solutions Ltd. on Vimeo.

The Macro

Let’s say we’d like to write a function that returns the member names in a specific type without using reflection.

object TypesDemo extends App {

  class A {
    def a: Int = 42
    def b: String = "b"
  }

  class B {
    def beta: String = "beta"
  }

  println(methodNames[A])
  println(methodNames[B])
}

Running this code should print List(, a, b) and List(, beta). We need to implement the methodNames function. And this will be a job for a macro. (The macro will need to be defined in a different module than the modulethat contains the TypeDemo.)

Let’s start the Types module with just its skeleton.

import language.experimental.macros
import scala.reflect.macros.Universe

object Types {

  def methodNames[A]: List[String] = macro methodNames_impl[A]

  def methodNames_impl[A : c.WeakTypeTag](c: Context): c.Expr[List[String]] = {
    ???
  }

Notice the import of the language.experimental.macros, which enables macros, and then the definition of the methodNames[A] function and the methodNames_impl which mirrors it. Let’s now add the implementation of the macro:

object Types {

  def methodNames[A]: List[String] = macro methodNames_impl[A]

  def methodNames_impl[A : c.WeakTypeTag](c: Context): c.Expr[List[String]] = {
    import c.universe._

    val methods: List[String] = c.weakTypeOf[A].typeSymbol.typeSignature.
      declarations.toList.filter(_.isMethod).map(_.name.toString)

    val listApply = Select(reify(List).tree, newTermName("apply"))

    c.Expr[List[String]](Apply(listApply, 
      List(methods.map(x => Literal(Constant(x))):_*)))
  }

}

The methods variable looks complex, but it is the well-known Scala collections dance. The interesting part is what follows. We define listApply to be a “pointer” to the apply function in List. We then return an expression containing List[String] by applying the listApply method to its parameters. If you examine the List.apply method, you’ll see that it takes one parameter of type A*. And so, the second parameter to Apply must be a list containing a single element (we have one parameter), and its value must be varargs representing the method names. Hence, we arrive at c.Expr[List[String]](Apply(listApply, List(methods.map(x => Literal(Constant(x))):_*))).

Running the TypesDemo now really prints the desired output.

Debugging

I’ve decided to write slightly more complex macro to make it meaningful to debug. Of course, you can't just add a breakpoint to the macro and expect to hit the breakpoint when you run your program. Macros run at compile time. And so, if we want to debug the macro, then instead of debugging our program, we must debug the Scala compiler.

In other words, the class that we’re running is the scala.tools.nsc.Main with some settings, namely:

  • -Dscala.usejavacp=true VM parameter
  • -cp types.Types demo/src/main/scala/types/TypesDemo.scala parameters

In IntelliJ IDEA, the debug settings for our example are

macdeb

See Forrester’s Report, “Vendor Landscape, Application Performance Management” to identify the right vendor to help IT deliver better service at a lower cost, brought to you in partnership with BMC.

Topics:

Published at DZone with permission of Jan Machacek, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}