Making It Easy with Lofty

One of the exciting new features of Scala 2.10 is Dynamic types, which enable you to write code like this:

scala> import scala.language.dynamics
import scala.language.dynamics

scala> class StraightToConsole extends Dynamic {
  |  def applyDynamic(name: String)(args: Any*) = println(name + ": " + args)
   | }
defined class StraightToConsole

scala> var target = new StraightToConsole()
target: StraightToConsole = StraightToConsole@35d3694a

scala> target.a_method_we_never_defined("argument 1", "argument 2")
a_method_we_never_defined: WrappedArray(argument 1, argument 2)

The classic use for this kind of dynamic dispatch is to implement a proxy objects which converts method calls into API requests (for example, binding to an XML/RPC service). More generally, dynamic dispatch can be a way of embedding a small, dynamically interpreted DSL into a typed language like Scala. It is up to the Dynamic target object, rather than the compiler, to decide what each method call “means”, and behave accordingly. For example, we could base our behaviour entirely on the length of the method name:

scala>import scala.language.dynamics

scala> class FizzBuzz extends Dynamic {
  |  def applyDynamic(name: String)(args: Any*) = if (name.length % 2 == 0) println("Fizz") else println("Buzz")
  | }
defined class FizzBuzz

scala> var target = new FizzBuzz()
target: FizzBuzz = FizzBuzz@504c2683

scala> target.fizz()

scala> target.fez()

The Lofty library uses this approach to implement a DSL for building up templates which can be used to instantiate complex objects. The basic idea is that a template is a collection of name/value pairs which describes the object we would like to build. Each object type has a builder, which knows how to convert this collection of properties into an object of that type. Both templates and objects are immutable, but templates can be built up in stages by adding new properties to (or overriding the existing properties of) an existing template. For example, here is a template for a Person:

val personTemplate = for {
  address <- builder[Address]
  _       <- address lines Seq("12 Maudlin Street", "Manchester")
  _       <- address postcode("VB6 5UX")

  person <- builder[Person]
  _       <- person name "Stephen Patrick"
  _       <- person address address
} yield person

We can create an instance of Person by requesting the object specified by this template, using buildFrom:

val person = buildFrom(personTemplate)

We can also extend the template with new and modified properties:

val newPersonTemplate = for {
  person <- personTemplate
  _      <- person name "John Maher"
  _      <- person age 43
} yield person

Users of tools such as factory_girl and MakeItEasy should recognise the intention here, which is to simplify building objects for test fixtures. The approach taken by Lofty is interesting not only because of the Dynamic magic, but because it embeds an imperative DSL within a pure functional, immutable context. If you’ve ever written any Visual Basic, you’ll be familiar with this pattern of object initialization:

Dim obj As Foo
Set obj = new Foo
with obj
  .property1 = "Apple"
  .property2 = 42
end with

This is a dangerous way to initialize objects, because if you fail to set all the required properties you’re left with a reference to an object in an invalid state. However, it does enable objects to be initialized in stages, with one stage setting up common features and a later stage configuring the object for a particular purpose. In a language where objects have immutable properties, which are initialized in the object’s constructor, this is no longer possible.

Lofty uses the scalaz State Monad to create a DSL in which properties can be written (and read) imperatively. This is what the use of the for syntax (which may look slightly strange to those not used to monadic programming in Scala) is all about. Not only can we write and overwrite values, we can also read values that we’ve previously assigned:

val newPersonTemplate = for {
  person1 <- personTemplate
  person2 <- personTemplate
  _       <- person1 name "Identical twins"
  _       <- person2 name
} yield (person1, person2)

(There’s a slight wrinkle here, which is that acts as a link to the name property of person1, rather than a copy of its value. If we subsequently change person1‘s name in the template, person2‘s name will change as well. This is because the actual value of isn’t known until the entire template is evaluated by buildFrom – the reference to here acts as a placeholder that is filled in at build-time with the real value, which is always the last value that was assigned to person1‘s name property.)

This illustrates the expressive power of Scala’s monadic for syntax: all of the above code is pure functional code that performs no mutation of any values, and yet we are able to use the State monad to embed an imperative sub-language within this context. Because nothing is really being mutated, we can create a “base” template, create a second that updates some of its values, and continue to use the unmodified “base” without having to worry that its behaviour has been changed.

Lofty is a work in progress; it would be nice in the future to be able to build case classes directly from templates using reflection, rather than having to implement an implicit Builder[T] for each type that we want to construct. However, it’s also a short and nifty demonstration of some “advanced” Scala techniques, employed in the service of making it easy for library users to accomplish a particular type of task. It may be that a Scala project of any size will live or die by its DSLs – by its ability to reduce complexity locally, by inventing an appropriate idiom for each different kind of programming task. Monads and dynamic typing are two of the powerful methods Scala provides to DSL-authors, and the ease with which a library like Lofty could be put together is an encouraging sign of the ability of the language to meet the challenges of programming in the large.