Wednesday, May 13, 2009

Sweet Scala Injection

Before getting back to non-final finals, let's consider a fun diversion. The second most famous equation in Physics is Newton's second law of motion, F = ma. When you apply a force F to an object of mass m, it accelerates at rate a.

Of course, your arithmetic only gives you the right answer if you're consistent in the measurement system you pick. There are two major systems of units in use. One is the metric system or SI (System International), formerly called the mks system. The letters stand for meter, kilogram, and second, which are the principal units used to measure length, mass, and time.

The other main system in use is also the metric system. (Gotcha.) It's called the cgs system, whose letters stand for centimeter, gram, and second. You have to take care to keep your units straight to use formulas like F = ma. The units for force are named newtons in the mks system and dynes in the cgs system. But if you multiply a gram times an acceleration recorded in meters per second per second, you'll get neither a newton nor a dyne.

The recent loss of the Mars Polar Lander is a painful demonstration that units really matter.

Since I like to blog about how thinking like a scientist makes me a better coder, I'll mention that it's unnatural to think, "oh, this book weighs two." Such a sentence might be grammatically correct, but without specifying the units, it's meaningless.

Scala has an especially thoughtful type system, and we can press it into service to keep our units straight when we do calculations. In this (and the next) post, we'll create a toy program, in illustrate one or two Scala goodies.

Kilograms and grams both measure mass. It's not too much of a stretch to use the "is-a" relationship in an object oriented language to capture this notion. In what follows, Kilograms and Grams inherit from Mass.

absract class Mass {
def kilograms: Double
}

class Kilograms(kg: Double) extends Mass {
def kilograms = kg
}

class Grams(grams: Double) extends Mass {
def kilograms = grams / 1000.0
}

Our base class has a kilograms method that returns the amount of mass in the mks units. All our calculations will be done in mks units, but the programmer is free to initialize a mass variable with either kilograms or grams.

Now let's construct a Force class. In a full-fledged example, we'd probably make it an abstract class extended by Newtons and Dynes. But we don't need such a complete solution here to demonstrate the ideas. Give the class an accelerates method, which tells how much the given force in newtons will accelerate a specified mass.

class Force(newtons: double) {
def accelerates(mass: Mass) =
(newtons / mass.kilograms) + " meters per sec^2"
}

Note that the accelerates method doesn't care whether it's passed a value in kilograms or in grams. All it's demanding is a mass, and since that offers a method to take us into mks-land, we can assuredly report our acceleration in meters per second per second.

Now, let's define a force of half a newton, and run a little program to see how much this force will accelerate a couple of masses. In each case below, there's no ambiguity about whether each mass is expressed in kilograms or grams, because the units are explicitly specified.

object MyApp extends Application {
val force = new Force(0.5)
println(force accelerates (new Kilograms(4.0)))
println(force accelerates (new Grams(100)))
//
// "0.125 meters per sec^2"
// "5.0 meters per sec^2"
}

The parentheses around the "new Kilograms(4.0)" are actually redundant, but that might surprise a Java programmer. Scala also lets us omit the dot between force and accelerates, which arguably improves readability.

So, the above works, but specifying "new Kilograms" everywhere I need to define a mass is a hassle. More importantly, it hurts readability, because there is no "new" anywhere in my mental model of the F = ma equation.

Fortunately, Scala offers injections, which can pretty up the source code. In C++, I can construct an instance on the stack without calling new. Although all instances in Scala live on the heap, I find the syntax reminiscent of C++ constructors.

We want to be able to write "Kilograms(4.0)" instead of "new Kilograms(4.0)" when we use our concrete Mass classes. To do this, create a Scala companion object of the same name as the class, and give it an apply method.

object Kilograms {
def apply(kg: Double) = new Kilograms(kg)
}

object Grams {
def apply(grams: Double) = new Grams(grams)
}

These functions are called injections. Basically, they are factory methods on the companion objects, but we don't need to call apply explicitly. This is the same syntactic sugar that allows us to write "List(1, 2, 3)" instead of "new List(1, 2, 3)". It pretties up our code nicely.

println(force accelerates Kilograms(4.0))
println(force accelerates Grams(100))

Note that we have made a tradeoff for this sweetness. We had to write more code (the injections) when defining our classes, so we could make life easier on the users of the classes. However, this is almost always the way to go. Readability is important.

Readability is also the reason that the accelerates method takes a Mass instance and not a plain Double. The extra word "Kilograms" or "Grams" doesn't help the computer, but it does help the human.

(However, the astute reader will have noticed that the kilograms method of the Grams class is inefficient. It performs a double precision floating point calculation every time it is called, even though the instance itself is immutable. If only there were a way to save the result of the calculation instead of the inputs, then we could run faster without worsening our memory footprint. Contemplating this is a topic for another day.)

In conclusion, tastefully applied Scala injections enhance readability. And they're more digestible than Martian soil coming towards you at a rate of, uhm, really fast.

No comments: