When macros first showed up in Scala as an experimental language feature last year, many Scala developers responded with skepticism or distaste. They argued that macros were a distraction from work on more urgent problems with the language, that they would lead to even more complex and reader-unfriendly code, etc.

This post is not about a useful application of macros. It’s inspired by a coupleof questions on Stack Overflow today, and is an example of exactly the kind of thing macros should not ever be used for. But it’s Friday evening and I’m drinking beer in the office and I think this trick is pretty clever, so here we go.

Suppose we’ve got a Scala class with some mutable fields:

caseclassCar(var speed: Int,var color: String,var position: Int = 0)

Also suppose we want to be able to write something like this:

val car = newCar(0, "blue")
car set {
color = "red"
speed = 100
}

Apparently some kind of syntax along these lines is possible in charming languages like Groovy and Visual Basic.

We can get close in Scala by adding a set method to Car:

defset(f: (Car) => Unit) = f(this)

And now we can write the following:

car set { c =>
c.color = "red"
c.speed = 100
}

But suppose we’re not happy about those nine or ten extra characters, or that we don’t want the overhead of the extra function application. The problem is that color and speed don’t mean anything on their own. The argument to the macro needs to typecheck before the macro is expanded, so we can’t for example write a macro that would just prepend import car._ to the block (as proposed here).

If we had untyped macros, this would be easy, but they’ve been thrown out of paradise. We’re not completely out of luck with plain old def macros, though, since it’s possible to use them to introduce structural types with arbitrarily named and typed methods. This means we can bring some dummy methods into scope that have the same shape as the setters for Car. The compiler will see these methods when it typechecks the macro argument, and then our macro implementation can rewrite them to be calls to the real setters on Car.

Before we see my implementation of this horrible idea, let’s make the problem harder. Suppose we also have some methods on Car that change its state: