(Update 12/23/2008: Thanks to Apostolos Syropoulos for pointing out an earlier reference for the concept of “traits”).
Because of all the recent hoo-ha about functional programming (e.g., as a “cure” for the multicore problem), I decided to cast aside my dysfunctional ways and learn one of the FP languages. The question was, which one?
My distinguished colleague, Michael Feathers, has been on a Haskell binge of late. Haskell is a pure functional language and is probably most interesting as the “flagship language” for academic exploration, rather than production use. (That was not meant as flame bait…) It’s hard to underestimate the influence Haskell has had on language design, including Java generics, .NET LINQ and F#, etc.
However, I decided to learn Scala first, because it is a JVM language that combines object-oriented and functional programming in one language. At ~13 years of age, Java is a bit dated. Scala has the potential of replacing Java as the principle language of the JVM, an extraordinary piece of engineering that is arguably now more valuable than the language itself. (Note: there is also a .NET version of Scala under development.)
Here are some of my observations, divided over three blog posts.
First, a few disclaimers. I am a Scala novice, so any flaws in my analysis reflect on me, not Scala! Also, this is by no means an exhaustive analysis of the pros and cons of Scala vs. other options. Start with the Scala website for more complete information.
A Better OOP Language
Scala works seamlessly with Java. You can invoke Java APIs, extend Java classes and implement Java interfaces. You can even invoke Scala code from Java, once you understand how certain “Scala-isms” are translated to Java constructs (javap
is your friend). Scala syntax is more succinct and removes a lot of tedious boilerplate from Java code.
For example, the following Person
class in Java:
class Person {
private String firstName;
private String lastName;
private int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public void setFirstName(String firstName) { this.firstName = firstName; }
public void String getFirstName() { return this.firstName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public void String getLastName() { return this.lastName; }
public void setAge(int age) { this.age = age; }
public void int getAge() { return this.age; }
}
can be written in Scala thusly:
class Person(var firstName: String, var lastName: String, var age: Int)
Yes, that’s it. The constructor is the argument list to the class, where each parameter is declared as a variable (var
keyword). It automatically generates the equivalent of getter and setter methods, meaning they look like Ruby-style attribute accessors; the getter is foo
instead of getFoo
and the setter is foo =
instead of setFoo
. Actually, the setter function is really foo_=
, but Scala lets you use the foo =
sugar.
Lots of other well designed conventions allow the language to define almost everything as a method, yet support forms of syntactic sugar like the illusion of operator overloading, Ruby-like DSL’s, etc.
You also get fewer semicolons, no requirements tying package and class definitions to the file system structure, type inference, multi-valued returns (tuples), and a better type and generics model.
One of the biggest deficiencies of Java is the lack of a complete mixin model. Mixins are small, focused (think Single Responsibility Principle ...) bits of state and behavior that can be added to classes (or objects) to extend them as needed. In a language like C++, you can use multiple inheritance for mixins. Because Java only supports single inheritance and interfaces, which can’t have any state and behavior, implementing a mixin-based design has always required various hacks. Aspect-Oriented Programming is also one partial solution to this problem.
The most exciting OOP enhancement Scala brings is its support for Traits, a concept first described here and more recently discussed here. Traits support Mixins (and other design techniques) through composition rather than inheritance. You could think of traits as interfaces with implementations. They work a lot like Ruby modules.
Here is an example of the Observer Pattern written as traits, where they are used to monitor changes to a bank account balance. First, here are reusable Subject
and Observer
traits.
trait Observer[S] {
def receiveUpdate(subject: S);
}
trait Subject[S] {
this: S =>
private var observers: List[Observer[S]] = Nil
def addObserver(observer: Observer[S]) = observers = observer :: observers
def notifyObservers() = observers.foreach(_.receiveUpdate(this))
}
In Scala, generics are declared with square brackets, [...]
, rather than angled brackets, <...>
. Method definitions begin with the def
keyword. The Observer
trait defines one abstract method, which is called by the Subject
to notify the observer of changes. The Subject
is passed to the Observer
.
This trait looks exactly like a Java interface. In fact, that’s how traits are represented in Java byte code. If the trait has state and behavior, like Subject
, the byte code representation involves additional elements.
The Subject
trait is more complex. The strange line, this: S =>
, is called a self type declaration. It tells the compiler that whenever this
is referenced in the trait, treat its type as S
, rather than Subject[S]
. Without this declaration, the call to receiveUpdate
in the notifyObservers
method would not compile, because it would attempt to pass a Subject[S]
object, rather than a S
object. The self type declaration solves this problem.
The next line creates a private list of observers, initialized to Nil
, which is an empty list. Variable declarations are name: type
. Why didn’t they follow Java conventions, i.e., type name
? Because this syntax makes the code easier to parse when type inference is used, meaning where the explicit :type
is omitted and inferred.
In fact, I’m using type inference for all the method declarations, because the compiler can figure out what each method returns, in my examples. In this case, they all return type Unit
, the equivalent of Java’s void
. (The name Unit
is a common term in functional languages.)
The third line defines a method for adding a new observer to the list. Notice that concrete method definitions are of the form
def methodName(parameter: type, ...) = {
method body
}
In this case, because there is only one line, I dispensed with the {...}
. The equals sign before the body emphasizes the functional nature of scala, that all methods are objects, too. We’ll revisit this in a moment and in the next post.
The method body prepends the new observer object to the existing list. Actually, a new list is created. The ::
operator, called “cons”, binds to the right. This “operator” is really a method call, which could actually be written like this, observers.::(observer)
.
Our final method in Subject
is notifyObservers
. It iterates through observers and invokes the block observer.receiveUpdate(this)
on each observer. The _
evaluates to the current observer reference. For comparison, in Ruby, you would define this method like so:
def notifyObservers()
@observers.each { |o| o.receiveUpdate(self) }
end
Okay, let’s look at how you would actually use these traits. First, our “plain-old Scala object” (POSO) Account
.
class Account(initialBalance: Double) {
private var currentBalance = initialBalance
def balance = currentBalance
def deposit(amount: Double) = currentBalance += amount
def withdraw(amount: Double) = currentBalance -= amount
}
Hopefully, this is self explanatory, except for two things. First, recall that the whole class declaration is actually the constructor, which is why we have an initialBalance: Double
parameter on Account
. This looks strange to the Java-trained eye, but it actually works well and is another example of Scala’s economy. (You can define multiple constructors, but I won’t go into that here…).
Second, note that I omitted the parentheses when I defined the balance
“getter” method. This supports the uniform access principle. Clients will simply call myAccount.balance
, without parentheses and I could redefine balance
to be a var
or val
and the client code would not have to change!
Next, a subclass that supports observation.
class ObservedAccount(initialBalance: Double) extends Account(initialBalance) with Subject[Account] {
override def deposit(amount: Double) = {
super.deposit(amount)
notifyObservers()
}
override def withdraw(amount: Double) = {
super.withdraw(amount)
notifyObservers()
}
}
The with
keyword is how a trait is used, much the way that you implement
an interface in Java, but now you don’t have to implement the interface’s methods. We’ve already done that.
Note that the expression, ObservedAccount(initialBalance: Double) extends Account(initialBalance)
, not only defines the (single) inheritance relationship, it also functions as the constructor’s call to super(initialBalance)
, so that Account
is properly initialized.
Next, we have to override the deposit
and withdraw
methods, calling the parent methods and then invoking notifyObservers
. Anytime you override a concrete method, scala requires the override
keyword. This tells you unambiguously that you are overriding a method and the Scala compiler throws an error if you aren’t actually overriding a method, e.g., because of a typo. Hence, the keyword is much more reliable (and hence useful…) than Java’s @Override
annotation.
Finally, here is an Observer
that prints to stdout when the balance changes.
class AccountReporter extends Observer[Account] {
def receiveUpdate(account: Account) =
println("Observed balance change: "+account.balance)
}
Rather than use with
, I just extend the Observer
trait, because I don’t have another parent class.
Here’s some code to test what we’ve done.
def changingBalance(account: Account) = {
println("==== Starting balance: " + account.balance)
println("Depositing $10.0")
account.deposit(10.0)
println("new balance: " + account.balance)
println("Withdrawing $5.60")
account.withdraw(5.6)
println("new balance: " + account.balance)
}
var a = new Account(0.0)
changingBalance(a)
var oa = new ObservedAccount(0.0)
changingBalance(oa)
oa.addObserver(new AccountReporter)
changingBalance(oa)
Which prints out:
==== Starting balance: 0.0
Depositing $10.0
new balance: 10.0
Withdrawing $5.60
new balance: 4.4
==== Starting balance: 0.0
Depositing $10.0
new balance: 10.0
Withdrawing $5.60
new balance: 4.4
==== Starting balance: 4.4
Depositing $10.0
Observed balance change: 14.4
new balance: 14.4
Withdrawing $5.60
Observed balance change: 8.8
new balance: 8.8
Note that we only observe the last transaction.
Download Scala and try it out. Put all this code in one observer.scala
file, for example, and run the command:
scala observer.scala
But Wait, There’s More!
In the next post, I’ll look at Scala’s support for Functional Programming and why OO programmers should find it interesting. In the third post, I’ll look at the specific case of concurrent programming in Scala and make some concluding observations of the pros and cons of Scala.
For now, here are some references for more information.
- The Scala website, for downloads, documentation, mailing lists, etc.
- Ted Neward’s excellent multipart introduction to Scala at developerWorks.
- The forthcoming Programming in Scala book.