Another Tour of Scala

AdvancedScalaObjects

The Gist

The tour doesn’t include this detail on what Scala objects are used for.

My Interpretation

You may recall that creating a case class automatically creates a singleton object of the same name that can be used to create and “de-create” (in a case statement) instances of that class.

This ties together the two main purposes of a singleton object: as a factory and as an extractor.

Factory

Suppose we want to be able to create a Person object from either a known first name and last name, or a “friendly” string that takes a full name. In Java, you might create multiple constructors. While you can also do so in Scala, multiple constructors are a bit cumbersome, syntax-wise, so a factory is a bit easier (as an aside, Ruby does not allow multiple constructors, so you would need this pattern there as well).

Note that this is almost entirely convention. The only real compiler “magic” that happens is in Scala’s special treatment of the apply() method. Given a reference xxx, Scala treats xxx(yyy) the same as xxx.apply(yyy). In this case, the object Person has two apply methods, each returning an instance of Person.

We could define a class Cat that extends Person (since all cats seem to think they are people), who gets his last name from his owner’s last name, and modify our Person object to render Cat objects sometimes:

Extractor

The more powerful use of objects is via a companion method to apply, called unapply, which Scala treats specially in a case statement. We saw in CaseClasses that you can “extract” the elements of an object as part of a case statement. In those examples, we created case classes using the case keyword. This tells Scala to automatically create some canonical structures for us. We could create those ourselves, however.

Suppose we extend our Person class to have an optional middle name:

class Person(val last:String, val first:String, val middle:String)

Now, suppose we wanted to match on people without a middle name:

person match {
 case Person(last,first,middle) => println("has middle name")
 case PersonNoMiddle(last,first) => println("no middle name")
}

With ordinary case classes, we cannot do this. But it is possible by understanding how case classes work. The expression between case and => in the code above is telling Scala to call the unapply method of the Person object (or of the PersonNoMiddle object).

When Scala encounters the first case statement, it reads it as “if there is a method apply of object Person that takes a object of type Person and returns an Option[Tuple3[String,String,String]], call that method and if the Option it returns is not None, set last, first, and middle to the tuple’s values and execute the code to the right of the =>. Otherwise, proceed to the next case statement and start over”.

That’s quite a mouthful. And it may take a second to sink in, but this is what’s going on under the covers of Scala’s powerful case classes and PatternMatching .

Fortunately, you don’t need to implement a lot of unapply methods; rather you take advantage of the case keyword creating them for you.

My Interpretation of This Feature

While this is a pretty advanced concept that might smack of a bit too much “magic”, it is actually quite logical and, more importantly, highly useful in aid of readable code.

It was hard to come up with succinct example that wasn’t artificial, but this is more of a “how does this work” type of concept than a “you need to know how to do this regularly”.

All that being said, I’m not sure why this feature couldn’t have been more formally tied to the concept (and definition) of a class.
I really like how in languages like Ruby or Objective-C, a symbol like Person is a constant that refers to the object representing the Person class. It just feels more OO to me. —Here, we have an object called Person that just happens to be related to the class Person through convention only. Perhaps this is fallout from the JVM’s internals?— In fact, an object named Person has the same access and privs to the Person class as objects of that class would.