Dynamic Types in Scala 2.10

This blog explores Scala dynamic types, a new feature of Scala 2.10, and provides some hopefully interesting exercises so you can try it out for yourself.

Why Dynamic Types in a Static Languages?

If you use Java or Scala instead of, say, Python or Clojure, it's probably because you like compile-time type checking. Personally, if I make a sloppy error, I prefer to have the bad news up front than discover it later through tedious debugging. Still, it's hard not to be envious when I see features such as Ruby's ActiveRecord ORM that peeks at the database and automatically makes classes out of tables, turning column names into field names. Without any boilerplate, the Ruby programmer can write empl.lname = "Doe".

I realize it's not like solving world hunger. In Java and Scala, I can perfectly well write empl.set("lname", "Doe"). But still, I am envious.

Scala, never a language to leave an enviable feature on the table, gives you that syntactic sugar in version 2.10. Specifically, when you call empl.lname = "Doe", the compiler converts it to a call empl.updateDynamic("lname")("Doe").

Of course, that's only permitted for certain types—otherwise, Scala stay statically typed.

How it Works

Dynamic types must extend the marker trait scala.Dynamic. You must also add the following line to the class that defines a dynamic type:

import scala.language.dynamics

(This is another part of Scala 2.10—if you use an obscure language feature, you need to tell the compiler with a special import directive. Users of dynamic types don't need to do this, just the implementors.) Finally, you implement one or more methods selectDynamic, updateDynamic (for dynamic fields) applyDynamic or applyDynamicNamed (for dynamic methods). Here, a dynamic field is a field whose name can be arbitrary (such as the column name of a database object). Similarly, a dynamic method has an arbitrary name that gets resolved at runtime—an example is in the next section.

Here are the details. Consider obj.name, where obj belongs to a class that's a subtype of Dynamic. Here is what the Scala compiler does with it.

  1. If name is a known method or field of obj, it is processed in the usual way.
  2. If obj.name is followed by (a1, a2, ...),
    1. If none of the arguments are named (of the form n=v), pass the arguments on to applyDynamic:
      obj.applyDynamic("name")(a1, a2, ...)
    2. If at least one of the arguments is named, pass the (name, value) pairs on to applyDynamicNamed:
      obj.applyDynamicNamed("name")((n1, v1), (n2, v2), ...)

      Here, n1, n2, and so on, are strings with the argument names, or "" for unnamed arguments.

  3. If obj.name is to the left of an =, call
    obj.updateDynamic("name")(rightHandSide)
  4. Otherwise call
    obj.selectDynamic("sel")

There are some gory issues with type parameters and sequence arguments—see the spec proposal for that.

So, if we wanted to build an ActiveRecord-like ORM, we'd produce a class Active with methods selectDynamic and updateDynamic (which take care of obj.name as an rvalue and lvalue), similar to the usual apply and update methods for lists and maps.

One of the most popular features of my book Scala for the Impatient are the exercises, so here's one:

Exercise 1: Implement an object CSV so that you can do the following:

val empl = CSV.findById("Employee", 42)
// Read Employee.csv
// First row has column labels; one of them must be id
println(empl.lname)
empl.lname = "Doe"
CSV.save("Employee") 

Just like a real ORM, you need to cache the objects. Each object belongs to some class (such as CSV) with fields for the table name and a hash map. The selectDynamic and updateDynamic read and update the map values.

Exercise 2. It would be nicer if objects from different tables belonged to different classes, like with the Ruby ORM. You can't imitate this exactly in Scala because class creation isn't a runtime event. How close can you come? The class user will define something like class Employee extends CSV. You need to find a way to read the proper CSV file into a pool of Employee objects, and save them later. Call getClass? Use generics? Your choice.

An Application: A Rubyesque XML Builder

Ruby has an XML builder that lets you generate XML like this:

xml.date {
   xml.year "2006"
   xml.month "01"
   xml.day "01"
} # Yields <date><year>2006</date><month>01</month><day>01</day><date>

Hashes are used for attributes:

xml.hello("World!", "type" => "global") # Yields <hello type="global">World!</hello>

Can we imitate this in Scala? As in Ruby, let's make the tag names dynamic method calls on the builder object. We can use  a variable sequence of pairs for the attributes.

xml.img("src" -> "http://horstmann.com/cay-tiny.gif", 
         "alt" -> "a tiny image of the author")

In Ruby, any (!) method can have an optional block argument, but in Scala, it's not easy to have an optional block after a variable argument list. I worked with Martin Christensen, a visiting scholar in our department, explored various syntactical possibilities. He settled on having the attributes after the element content.

He implemented a method

def applyDynamic(name: String)(block: => Any, args: (String, Any)*): Any

so that you can generate XML like this:

val searchLinks = List(
  "Google" -> "http://www.google.com",
  "Ask.com" -> "http://www.ask.com",
  "Yahoo!" -> "http://www.yahoo.com",
  "Alta Vista" -> "http://www.altavista.com")

val xml = new XmlBuilder
xml html {
  xml head {
    xml title "Search Links"
  }
  xml body {
    xml p ("Search Links:", "style" -> "color: red")
    xml ul ({
      for ((name, link) <- searchLinks)
        xml li {
          xml a (name, "href" -> link)
        }
    }, "id" -> "searchList")
  }
}

println(xml.getXml)

All those xml inside are a little unsightly, but you can't avoid them. A dynamic method must be called on a dynamic object. We tried using an anonymous subclass of a dynamic class, hoping that the dynamic methods would be called on this, but it didn't work.

Exercise 3: Implement the XmlBuilder class. Hint: In the applyDynamic method, concatenate the start tag and attributes to a mutable String field, call the block and concatenate its result if it's a string, then concatenate the end tag. The getXml method just returns that field.

Exercise 4: Make the attributes come first. Use Currying to separate the attribute list from the block. Use an empty default for the block.

Exercise 5: Generate genuine Scala XML (i.e. a scala.xml.Node, not a String).

Of course, we aren't proposing this as a replacement for the native Scala XML syntax. It's just an example to get you thinking about dynamic methods.