Wednesday, August 30, 2006

A taste of Scala

I've recently been trying to learn Scala, a programming language developed at a Swiss university. It has many (many!) cool features, such as seamless interoperability with Java - a result of being compiled to JVM bytecodes -, strong support for functional programming, sophisticated object oriented characteristics and a strong static type system. Rather than continue listing the language's capabilities I will, instead, share a personal use case.

OK, so one of the first items things I looked at was the unit testing framework, SUnit. It comes bundled in the standard library in the scala.testing.SUnit package and is dead simple. I hope the following snippet is self-explanatory enough:
object StackTest {
import scala.testing.SUnit._

def main(args:Array[String]): Unit = {
val tr = new TestResult
new TestSuite(
new Test01,
new Test02,
).run(tr)

for(val f <- tr.failures())
Console println f
}

class Test01 extends TestCase("pushing an element onto an empty stack") {
override def runTest() = {
val stack = new Stack()
val element = "asdf"
stack push element
assertEquals(stack.peek(), element)
}
}

class Test02 extends TestCase("popping an element from a stack") {
override def runTest() = {
val stack = new Stack()
val element = "asdf"
stack push element
stack.pop()
assertEquals(stack.isEmpty, true)
}
}
}

It really is pretty simple; the whole testing framework sits on a single 200-line file. But is also is a bit verbose, isn't it? All those inner classes, cluttering the code... I tried to simplify things a bit. Here is what I came up with:
import sorg.testing._;

object StackTests extends Tests with ConsoleDriver {
test ("pushing an element onto an empty stack") {
val stack = new Stack()
val element = "asdf"
stack push element
assertEquals(stack.peek(), element)
}

test ("popping an element from a stack") {
val stack = new Stack()
val element = "asdf"
stack push element
stack.pop()
assertEquals(stack.isEmpty, true)
}
}
I think it looks better. Sort of like those DSLs that are so fashionable these days... But the really cool thing is that it only took a couple dozen lines of code and a couple of hours to extend SUnit. Mind you that someone really proficient in Scala could probably do it much more quickly. See the whole unit testing domain specific language: (the name is almost larger than the code itself :)
package sorg.testing;
import scala.testing.SUnit._;

abstract class Tests extends Test with Assert {
type TestExp = () => Unit;
var tests = List[Pair[String,TestExp]]();

def test(desc: String)(t: => Unit) : Unit = {tests = Pair(desc,()=>t) :: tests};

override def run(tr: TestResult) = {
for (val Pair(desc, expression) <- tests) new TestCase(desc) {
override def runTest() = {Console println "running (" + desc + ")"; expression()}
}.run(tr)
}
}
trait ConsoleDriver extends Test {
def main(args:Array[String]): Unit = {
val results = new TestResult
Console println "running tests..."

this.run( results )

if (!results.failures.hasNext)
Console println "Success!";
else {
Console println "The following tests failed:";
for(val each:TestFailure <- results.failures)
Console println (each.toString + ":\n" + each.trace);
}
}
}

Expressiveness and power, what more can one ask of a programming language?

2 comments:

mishelangelo said...

thanx for sharing this code. I've been looking for/thinking of something similar too but i was using an array of functions since i'm not that cool with currying yet. Yours is definitely a nice approach!

Rafael de F. Ferreira said...

@mishelangelo:
Thanks. You should check out the newer scala libraries for testing:
http://rehersal.sourceforge.net/ (looks good but seems to be stalled at the moment)

http://code.google.com/p/specs/ (used for behavior driven development, integrates with ScalaCheck)

http://code.google.com/p/scalacheck/ (has some sophisticated data generation and invariant verification functionality).