Archive for the ‘Scala’ Category
Scala: Case Classes
Scala’s case classes are regular classes with a twist. They are designed to support pattern matching without having to write excessive amounts of boilerplate code. As a developer, I want to understand how and when case classes should be used.
How are case classes declared?
By prefixing the declaration of a class with the word ‘case’. e.g.
case class Person(firstName: String, lastName: String)
How do case classes differ from regular classes?
The compiler will automatically do a few things for case classes:
- Generate an accessor methods for each of the constructor parameters
- For each of the constructor parameters that is not already marked with a val or var modifier, a val modifier will be inserted. In effect, this will generate accessors for all of the constructor parameters.
- Generate an extractor object
- The compiler will also generate an extractor object. This supports pattern matching and means that ‘new’ is not required to create a new instance of a case class, so the following is valid:
val mark = Person("Mark", "Thomas")
The compiler interprets a ‘Person(‘ as a call to ‘Person.apply’.
- Generate equals, hashcode and toString methods
- Two instances are considered equal if they belong to the same case class and each of the constructor arguments is equal according to their equals method. The hashCode is guaranteed to match if the hashCodes of the constructor members of the two instances match. And, the toString method will do something sensible
How do case classes help in pattern matching?
The auto-generated extractor can be used with match to match on constructor arguments. You can match on some or all of the constructor arguments, and decompose the case class back to those arguments. For example:
person match { case Person(firstName, "Thomas") => println("Hi " + firstName) case _ => println("Hello friend - " + person) }
When are case classes useful?
A quick Google search on ‘case classes’ reveals some controversy around two points: whether case classes can lead to over-use of the match statement breaking encapsulation, and whether case classes are needed at all given that there pattern matching abilities can be provided with just extractors alone.
Encapsulation
The argument goes that using match is not an object-oriented approach to development, and we’ve exposed our constructor parameters for no good reason. In the example above, an alternative approach could have been to add a ‘greet’ method to Person to encapsulate the behaviour of formulating the greeting inside the Person class. This is a valid concern and was my first concern when I thought about using case classes, however as Martin Odersky points out in a blog post there are times when ‘decomposition on the outside’ can be more preferable to ‘decomposing on the inside’. Or put another way, where a solution based on OO-decomposition can be less readable or more contrived than one that is not. From Martin’s post this is my favourite illustration of where pattern matching, based on type, can lead to a good solution:
try { ... } catch { case ex: IOException => "handle io error" case ex: ClassCastException => "handle class cast errors" case ex: _ => "generic recovery" }
We can’t put the recovery code in to the exception class because it is context dependent, and whilst we could probably do something clever with the visitor pattern it would be painful. Martin identifies two cases where he believes that ‘decomposition from the outside’ is preferable to ‘decomposition from the inside’:
- when a computation rule involves several objects
- when a computation cannot usefully be defined as a member of the class on which we want to differentiate
Switch statements are not inherently bad but they are easy to abuse, the key as developers is understanding the best way of decomposing your problem domain and choosing the best solution for your situation.
Are case classes needed?
On this I’m not sure. Pattern matching can be enabled using extractors alone, but in some cases where data encapsulation is not a concern then they can provide a convenient approach. I hope that as my experience with Scala increases, I will be able to better answer this question.
Other uses for case classes?
Case classes provide a good way of implementing the visitor pattern because they provide a way of doing multiple-dispatch. I’ll save that for another post.
Scala: Puzzlers
Ivan Tarasov has posted some interesting Scala puzzlers in two parts: part 1, part 2. Be sure to read the comments too, there are some interesting observations in there.
Scala: Dependency Injection Using Structural Typing
Scala has a powerful type system that enables different patterns for expressing dependencies compared with languages like Java or C#. Jamie Webb described an interesting approach using structural typing on the Scala User Mailing List (link to post).
Jamie injected a config object defined using a structural type. This has the benefit of clearly defining the dependencies of a class, whilst allowing the same environment object to be shared. Here is an example from Jamie’s post, which describes a coffee warmer with a dependency on a heater and a pot sensor, the code shown is a modified version from a blog post by Jonas Bonér which uses constructor-based dependency injection:
class Warmer(env: { val potSensor: SensorDevice val heater: OnOffDevice }) { def trigger = { if(env.potSensor.isCoffeePresent) env.heater.on else env.heater.off } }
A simple config object can then be defined instantiates the pot sensor, heater and wires up the warmer:
object Config { lazy val potSensor = new PotSensor lazy val heater = new Heater lazy val warmer = new Warmer(this) }
This seems like a nice pattern, it’s quite elegant, type-safe, easy to test and makes the dependencies of a class explicit. There are a couple of other patterns in this area, I’ll make further posts around those.
Scala: Traits and Self Types
Scala has a few features that, for those who come from a Java or C# background, provide new ways of modelling components and services. I’m going to write a few posts on this, but begin by describing some of the language features that enable them. First up is traits.
What is a trait?
A trait is a collection of fields and methods. Traits can be mixed in to classes to add new features or to modify behaviour. Scala’s Ordered trait is a good example of this:
trait Ordered[A] { def compare(that: A): Int def < (that: A): Boolean = (this compare that) < 0 def > (that: A): Boolean = (this compare that) > 0 def <= (that: A): Boolean = (this compare that) <= 0 def >= (that: A): Boolean = (this compare that) >= 0 def compareTo(that: A): Int = compare(that) }
By mixing-in Ordered to a new class, then you can get four useful operators by just implementing compare. This is quite powerful, for example:
class Money extends Ordered[Money] with SomeOtherTrait { ... def compare(that: Money) = { ... }
As classes can be composed of many traits, this creates a powerful way of building richer classes from simpler ones by layering in capabilities.
Self Types
Ordered can be mixed in to any class; it doesn’t depend on any methods or fields of the class that it is mixed in to. Sometimes it’s useful for a trait to be able to use the fields or methods of a class it is mixed in to, this can be done by specifying a self type for the trait. A self type can be specified for a class or a trait as follows:
trait SpellChecker { self => ... }
self within the context of this trait will refer to this. Aliasing this is useful for nested classes or traits where it would otherwise be difficult to access a particular this. The syntax can be extended to specify a lower-bounds on this, when this is done the trait or class can use the features of this lower-bound class, so it can extend or modify its behaviour.
trait SpellChecker { self: RandomAccessSeq[char] => ... }
The compiler will check that any class in a hierarchy including SpellChecker is or extends RandomAccessSeq[char], so SpellChecker can now use the fields or methods of RandomAccessSeq[char]
Scala: Type safe duck typing
If it walks like a duck and quacks like a duck, I would call it a duck.
Duck typing uses the set of methods and properties of an object, rather than its position in a type hierarchy to decide what you can do with it.
Scala’s structural typing feature provides something close to duck typing in a type safe way. A structural type is close to a .NET anonymous type; a type with a set of methods and properties but with no name. An example in Scala based on one of Wikipedia’s examples of duck typing:
class Duck { def quack = println("Quaaaaaack !") def feathers = println("The duck has white and gray feathers.") } class Person { def quack = println("The person imitates a duck.") def feathers = println("The person takes a feather from the ground and shows it.") } def inTheForest(duck: { def quack; def feathers }) = { duck.quack duck.feathers }
The inTheForest function declares that it will accept any object that has a quack and feathers method, both of which take no parameters and return Unit (like void in Java/C#). When executed, the code works as expected:
scala> inTheForest(new Person) The person imitates a duck. The person takes a feather from the ground and shows it. scala> inTheForest(new Duck) Quaaaaaack ! The duck has white and gray feathers.
To demonstrate the type safety, lets try using a string:
scala> inTheForest("Duck")
:6: error: type mismatch;
found : java.lang.String("Duck")
required: AnyRef{def quack: Unit; def feathers: Unit}
inTheForest("Duck")
^
When is this useful? Mostly in situations where you are consuming classes that follow common conventions but have no shared interface, in this case you can define the parts you are interested in by using the structural type as an implicit interface.
Finally, if you find that you are using a structural type a lot, it can define it in one place:
object UsefulTypes { type Duck = { def quack; def feathers } }
inTheForest can then be declared as:
import UsefulTypes.Duck def inTheForest(duck: Duck) = { duck.quack duck.feathers }
Polyglot programming with IntelliJ 8
Artima interviewed JetBrains’s Dmitry Jemerov at JavaOne 2008 back in June, the interview notes are here: http://www.artima.com/lejava/articles/javaone_2008_dmitry_jemerov.html.
Dmitry makes some interesting comments around Polyglot Programming and how Jetbrains plan to add new language support to IntelliJ. I’m glad to see a move towards more modularisation and particularly a way of specifying which features to enable (e.g. Java, SVN etc) and even for disabling Java when using another language. This change should go a long way to addressing the bloat, and sluggishness, of earlier IntelliJ releases.
The most exciting feature for me is Scala support. I’m a big fan of Scala, but I see two barriers to adoption: the available documentation is limited, particularly good examples; and it’s quite a complex language. Dmitry touched on this last point but I don’t really agree that it’s as complex as C++. Artima have a Scala book in the works which is a fantastic way to pick up the language.
The IntelliJ EAP releases can be downloaded from http://www.jetbrains.com/idea/nextversion/.