First steps in Scala for beginning programmers, Part 11

Topics: SBT, scalabha, packages, build systems

Preface

This is part 11 of tutorials for first-time programmers getting into Scala. Other posts are on this blog, and you can get links to those and other resources on the links page of the Computational Linguistics course I’m creating these for.

This tutorial gives an introduction to building Scala applications using SBT (the Simple Build Tool). This will be done in the context of the Scalabha package, which I have created for primarily for my Introduction to Computational Linguistics class. Some supporting code is available in Scalabha for some basic natural language processing tasks; most relevant at the moment is the code that is in Scalabha that supports the part-of-speech tagging homework for the class.

The previous tutorial showed how Scala code can be compiled with scalac and then run with scala. One problem we ended up with is that there were generated class files littering the working directory. Another thing we did not discuss is how a large system can be created in a modular way that organizes code and classes. For example, you might want to have code in different directories generate classes that can be used by one another. You also may want want to incorporate classes from other libraries into your own code. The solutions we’ll discuss to address these needs and more are build systems and packages.

Note: The tutorial assumes you are using some version of Unix. If you are on Windows, you should consider using Cygwin, or you could dual boot your computer.

Note: In this tutorial, I’ll assume you are using as simple text editor to modify files. However, note that the general setup you are working with here can be used from more powerful Integrated Developer Environements (IDEs) like Eclipse, IntelliJ, and NetBeans.

Setting up Scalabha

We’ll work with SBT, which is perhaps the most popular build tool for Scala.  The Scalabha toolkit mentioned earlier uses SBT (version 0.11.0), so we’ll discuss SBT in the Scalabha context.

The first thing you need to do is download Scalabha v0.1.1 Next unzip the file, change to the directory it unpacked to, and list the directory contents.

[sourcecode language=”bash”]
$ unzip scalabha-0.1.1-src.zip
Archive:  scalabha-0.1.1-src.zip
<lots of output>
$ cd scalabha-0.1.1
$ ls
CHANGES.txt README      build.sbt   project
LICENSE     bin         data        src
[/sourcecode]

Briefly, these contents are:

  • README: A text file describing how to install Scalabha on your machine.
  • LICENSE: A text file giving the license, which is the Apache Software License 2.0.
  • CHANGES.txt: A text file describing the modifications made for each version (not much so far).
  • build.sbt: A text file that contains instructions for SBT regarding how to build Scalabha
  • bin: A directory that contains the scalabha script, which will be used to run applications developed within the Scalabha build system and also to run SBT itself. It also contains sbt-launch-0.11.0.jar, which is a bottled up package of SBT’s classes that will allow us to use SBT very easily. There are some other files that are Perl scripts that are relevant for a research project and aren’t important here.
  • data: A directory containing part-of-speech tagged data for English and Czech that forms the basis for the fourth homework of my Introduction to Computational Linguistics course this semester.
  • project: A directory containing a single file “plugins.sbt” which tells SBT to use the Assembly plugin. More on this later.
  • src: The most important directory of all — it contains the source code of the Scalabha system, and is where you’ll be adding some code as you work with SBT.

At this point you should read the README and get Scalabha set up on your computer, including building the system from source. In this tutorial, I will give some extra details on using SBT and code development with it, complementing and extending the brief information given in the README.

Note that I will refer the environment variable SCALABHA_DIR below. As specified in the README, you should set this variable’s value to be where you unpacked Scalabha. For example, for me this directory is ~/devel/scalabha.

Tip: to make it so that you don’t have to set your environment variables every time you open a new shell, you can set environment variables in your ~/.profile (Mac, Cygwin) or ~/.bash_aliases (Ubuntu) files. For example, this is in my profile files on my machines.

[sourcecode language=”bash”]
export SCALABHA_DIR=$HOME/devel/scalabha
export PATH=$PATH:$SCALABHA_DIR/bin
[/sourcecode]

SBT: The Simple Build Tool

This is not a tutorial about setting up a project to use SBT — it is simply about how to use a project that is already set up for SBT. So, if you are looking for resources about learning SBT, what you’ll mainly find are resources to help programmers configure SBT for their project. These will likely confuse you (the Simple Build Tool is not so simple any more, when it comes to configuration). Using it is straightforward, but the kind of know-how that experienced coders have with using something like SBT is what you probably won’t find much help on. Here, I intend to give the basics so that you have a better starting point for doing more with SBT.

First off, there is a bit of slight of hand with Scalabha that could be confusing. Rather than having users install SBT themselves, I have put the jar file for SBT in the bin directory of Scalabha; then, the scalabha executable (in that same directory) can pick that up and use it to run SBT. (My students and I have set up a number of Scala/Java projects in this way, including Fogbow, Junto, Textgrounder, and Updown.) The scalabha executable has a number of execution targets (more on this later), and one of these is “build“. When you call scalabha’s build target, it invokes SBT and drops you into the SBT interface.

Do the following, in your SCALABHA_DIR.

[sourcecode language=”bash”]
$ scalabha build
[info] Loading project definition from /Users/jbaldrid/devel/scalabha/project
[info] Set current project to Scalabha (in build file:/Users/jbaldrid/devel/scalabha/)
>
[/sourcecode]

You could have achieved the same by downloading SBT and running it according to the instructions for SBT, but this setup saves you that trouble and ensures that you get the right version of SBT. It is just worth pointing out so that you don’t think that Scalabha is SBT –  SBT is entirely independent of Scalabha.

If you have had any trouble with the Scalabha setup, you can create an issue on the Scalabha Bitbucket site. That just means that I’ll get a notice that you had some problems and can hopefully help you out. And, it is possible that someone else will have had the same problem, in which case you might find your answer there. Most of the problems with this sort of setup are due to confusions about environment variables and unfamiliarity with command line tools.

Compiling with SBT

Let’s actually do something with SBT now. If you successfully got through the README, you will have already done what is next, but I’ll give some more details about what is going on.

Because you may have run some SBT actions already as part of doing the README, start out by running the “clean” action so that we’re on the same page.

[sourcecode language=”bash”]
> clean
[success] Total time: 0 s, completed Oct 26, 2011 10:18:08 AM
[/sourcecode]

Then, run the “compile” action.

[sourcecode language=”bash”]
> compile
[info] Updating {file:/Users/jbaldrid/devel/scalabha/}default-86efd0…
[info] Done updating.
[info] Compiling 13 Scala sources to /Users/jbaldrid/devel/scalabha/target/classes…
[success] Total time: 9 s, completed Oct 26, 2011 10:18:19 AM
[/sourcecode]

In another shell (which means another command line window), go to SCALABHA_DIR and list the contents of the directory. You’ll see that two new directories have been created, lib_managed and target. The first is where other libraries have been download from the internet and placed into the Scalabha project space so that they can be easily used — don’t worry about this for the time being. The second is where the compiled class files have gone. To see some example class files, do the following.

[sourcecode language=”bash”]
$ ls target/classes/opennlp/scalabha/postag/
BaselineTagger$$anonfun$tag$1.class
BaselineTagger.class
EnglishTagInfo$$anonfun$zipWithTag$1$1.class
<… many more class files …>
RuleBasedTagger$$anonfun$tag$2.class
RuleBasedTagger$$anonfun$tagWord$1.class
RuleBasedTagger.class
[/sourcecode]

These were generated from the following source files.

[sourcecode language=”bash”]
$ ls src/main/scala/opennlp/scalabha/postag/
HmmTagger.scala PosTagger.scala
[/sourcecode]

Open up PosTagger.scala in a text editor and look at it — you’ll see the class and object definitions that were the sources for the generated class files in the target/classes directory. Basically, SBT has conveniently handled the separation of source and compile class files so that we don’t have the class files littering our work space.

How does SBT know where the class files are? Simple: it is configured to look at src/main/scala and compile every .scala file it finds under that directory. In just a bit, you’ll start adding your own scala files and be able to compile and run them as part of the Scalabha build system.

Next, at the SBT prompt, invoke the “package” action.

[sourcecode language=”bash”]
> package
[info] Updating {file:/Users/jbaldrid/devel/scalabha/}default-86efd0…
[info] Done updating.
[info] Packaging /Users/jbaldrid/devel/scalabha/target/scalabha-0.1.1.jar …
[info] Done packaging.
[success] Total time: 0 s, completed Oct 26, 2011 10:19:02 AM
[/sourcecode]

In the shell prompt that we used to list files previously, list the contents of the target directory.

[sourcecode language=”bash”]
$ ls target/
cache              classes            scalabha-0.1.1.jar streams
[/sourcecode]

You have just created scalabha-0.1.1.jar, a bottled up version of the Scalabha code that others could use in their own libraries. The extension “jar” stands for Java Archive, and it is basically just a zipped up collection of a bunch of class files.

Scalabha itself uses another of supporting libraries produced by others. To see the jars that are used as supporting libraries by Scalabha, do the following.

[sourcecode language=”bash”]
$ ls lib_managed/jars/*/*/*.jar
lib_managed/jars/jline/jline/jline-0.9.94.jar
lib_managed/jars/junit/junit/junit-3.8.1.jar
lib_managed/jars/org.apache.commons/commons-lang3/commons-lang3-3.0.1.jar
lib_managed/jars/org.clapper/argot_2.9.1/argot_2.9.1-0.3.5.jar
lib_managed/jars/org.clapper/grizzled-scala_2.9.1/grizzled-scala_2.9.1-1.0.8.jar
lib_managed/jars/org.scalatest/scalatest_2.9.0/scalatest_2.9.0-1.6.1.jar
[/sourcecode]

Of course, you may still be wondering what it means to “use a library” in your code. More on this after we talk about packages and actually start doing some code ourselves.

Packages

Projects with a lot of code are generally organized into a package that has a set of sub-packages for parts of the code base that work closely together. At the very high level, a package is simply a way to ensure that we have unique fully qualified names for classes. For example, there is a class called Range in the Apache Commons Lang library and in the core Scala library. If you want to use both of these classes in the same piece of code, there is an obvious problem of a name conflict. Fortunately, they are contained within packages that allow us to refer to them uniquely.

  • Range in the Apache Commons Lang library is org.apache.commons.lang3.Range
  • Range in Scala is scala.collection.immutable.Range

So, when we do need to use them together, we are still able to do so without conflict. You’ve actually already seen some package names before, for example with java.lang.String and the distinction between scala.collection.mutable.Map and scala.collection.immutable.Map.

To see the packages and classes in Scalabha, run the “doc” action in SBT.

[sourcecode language=”bash”]
> doc
[info] Generating API documentation for main sources…
model contains 35 documentable templates
[info] API documentation generation successful.
[success] Total time: 7 s, completed Oct 26, 2011 10:22:23 AM
[/sourcecode]

Now, point your browser to the file target/api/index.html. Note: this means doing “open file” and then going to your SCALABHA_DIR and then to target, then to api, and then selecting index.html. You can then browse the packages and classes in Scalabha. For example, look at HmmTagger, which is in the package opennlp.scalabha.postag, and you’ll see some of the fields and functions that are made available by that class.

But, you may still be wondering: how do I use these packages and classes in my code anyway? We do so via import statements. We’ll explore this by creating our own source code and compiling it.

Creating and compiling new code in SBT

First, we’ll begin by just doing a simple hello world application that is done in the context of Scalabha and uses a package name. Get set up for this by doing the following set of commands.

Now, point your browser to the file target/api/index.html. Note: this means doing “open file” and then going to your SCALABHA_DIR and then to target, then to api, and then selecting index.html. You can then browse the packages and classes in Scalabha. For example, look at HmmTagger, which is in the package opennlp.scalabha.postag, and you’ll see some of the fields and functions that are made available by that class.

But, you may still be wondering: how do I use these packages and classes in my code anyway? We do so via import statements. We’ll explore this by creating our own source code and compiling it.

Creating and compiling new code in SBT

First, we’ll begin by just doing a simple hello world application that is done in the context of Scalabha and uses a package name. Get set up for this by doing the following set of commands.

[sourcecode language=”bash”]
$ cd $SCALABHA_DIR
$ cd src/main/scala/opennlp/
$ mkdir bcomposes
[/sourcecode]

Next, using a text editor, create the file Hello.scala in the src/main/scala/opennlp/bcomposes directory with the following contents.

[sourcecode language=”scala”]
package opennlp.bcomposes

object Hello {
def main (args: Array[String]) = println("Hello, world!")
}
[/sourcecode]

This is just like the hello world object from the previous tutorial, but now it has the additional package specification that indicates that its fully qualified name is opennlp.bcomposes.Hello.

Because the source code for Hello.scala is in a sub-directory of the src/main/scala directory, we can now compile this file using SBT. Make sure to save Hello.scala, and then go back to your SBT prompt and type “compile“.

[sourcecode language=”bash”]
> compile
[info] Compiling 1 Scala source to /Users/jbaldrid/devel/scalabha/target/classes…
[success] Total time: 1 s, completed Oct 26, 2011 10:35:15 AM
[/sourcecode]

Notice that it compiled just one Scala source: SBT has already compiled the other source files in Scalabha, so it only had to compile the new one that you just saved.

Having successfully created and compiled the opennlp.bcomposes.Hello object, we can now run it. The scalabha executable provides a “run” target that allows you to run any of the code you’ve produced in the Scalabha build setup. In your shell, type the following.

[sourcecode language=”bash”]
$ scalabha run opennlp.bcomposes.Hello
Hello, world!
[/sourcecode]

There is actually a bunch of stuff going on under the hood that ensures that your new class is included in the CLASSPATH and can be used in this manner (see bin/scalabha for details). This will simplify things for you considerable. To make a long story short, getting the CLASSPATH appropriately set is one of the main points of confusion for new developers; this way you can keep on moving without having to worry about what is essentially a plumbing problem.

Now, let’s say you want to change the definition of the Hello object to also print out an additional message that is supplied on the command line. Modify the main method to look like this.

[sourcecode language=”scala”]
def main (args: Array[String]) {
println("Hello, world!")
println(args(0))
}
[/sourcecode]

Now save it, and try running it.

[sourcecode language=”bash”]
$ scalabha run opennlp.bcomposes.Hello Goodbye
Hello, world!
[/sourcecode]

Oops — it didn’t work?! I’ve just forced you directly into a common point of confusion for students who are switching from scripting to compiling: you must compile before it can be used. So, invoke compile in SBT, and then try that command again.

[sourcecode language=”bash”]
$ scalabha run opennlp.bcomposes.Hello Goodbye
Hello, world!
Goodbye
[/sourcecode]

To see what happens when you produce a syntax error in your Scala code, go back to Hello.scala and change first print statement in the main method so that it is missing the last quote:

[sourcecode language=”scala”]
println("Hello, world!)
[/sourcecode]

Now go back to SBT and compile again to see the love letter you get from the Scala compiler.

[sourcecode language=”bash”]
[info] Compiling 1 Scala source to /Users/jbaldrid/devel/scalabha/target/classes…
[error] /Users/jbaldrid/devel/scalabha/src/main/scala/opennlp/bcomposes/Hello.scala:5: unclosed string literal
[error]     println("Hello, world!)
[error]             ^
[error] /Users/jbaldrid/devel/scalabha/src/main/scala/opennlp/bcomposes/Hello.scala:7: ‘)’ expected but ‘}’ found.
[error]   }
[error]   ^
[error] two errors found
[error] {file:/Users/jbaldrid/devel/scalabha/}default-86efd0/compile:compile: Compilation failed
[error] Total time: 0 s, completed Oct 26, 2011 11:02:07 AM
[/sourcecode]

The compile attempt failed, and you must go back and fix it. But don’t do that yet. There’s a handy aspect of SBT in this write-save-compile loop that saves you time and effort: SBT allows triggered executation of actions, which means that SBT can automatically perform an action if there is a change to the stuff it cares about. The compile action cares about the source code, so it can monitor changes in the file system and automatically recompile any time a file is saved. To do this, you simply add ~ in front of the action.

Before fixing the error, type ~compile into SBT. You’ll see the same error message as before, but don’t worry about that. The last line of output from SBT will say:

[sourcecode language=”bash”]
1. Waiting for source changes… (press enter to interrupt)
[/sourcecode]

Now go to Hello.scala again, add the quote back in, and save the file. This triggers the compile action in SBT, so you’ll see it automatically compile, with a success message.

[sourcecode language=”bash”]
[info] Compiling 1 Scala source to /Users/jbaldrid/devel/scalabha/target/classes…
[success] Total time: 0 s, completed Oct 26, 2011 11:02:49 AM
2. Waiting for source changes… (press enter to interrupt)
[/sourcecode]

This is a nice way to see if your code is compiling as you work on it, with very little effort. Every time you save the file, it will let you know if there are problems. And, you’ll also be able to use the scalabha run target and know that you are using the latest compiled version when you do so.

As you develop your code in this way, you can invoke the “doc” action in SBT, then reload the index.html page in your browser, and it will show you the updated documentation for the things you’ve created. Try it now and look at the opennlp.bcomposes package that you’ve now created.

Creating code that uses existing packages

Now we can come back to using code from existing packages. In the past (if you’ve gone through all of these tutorials), you’ve seen statements like import scala.io.Source. That came from the standard Scala library, so it is always available to any Scala program. However, you can also use classes developed by others in a similar manner, provided your CLASSPATH is set up such that they are available. That is exactly what SBT does for you: all of the classes that are defined in the src/main/scala sub-directories are ready for your use.

As an example, save the following code as src/main/scala/opennlp/bcomposes/TreeTest.scala. It constructs a standard phrase structure tree for the sentence “I like coffee.”

[sourcecode language=”scala”]
package opennlp.bcomposes

import opennlp.scalabha.model.{Node,Value}

object TreeTest {

def main (args: Array[String]) {
val leaf1 = Value("I")
val leaf2 = Value("like")
val leaf3 = Value("coffee")
val subjNpNode = Node("NP", List(leaf1))
val verbNode = Node("V", List(leaf2))
val objNpNode = Node("NP", List(leaf3))
val vpNode = Node("VP", List(verbNode, objNpNode))
val sentenceNode = Node("S", List(subjNpNode, vpNode))

println("Printing the full tree:n" + sentenceNode)
println("nPrinting the children of the VP node:n" + vpNode.children)

println("nPrinting the yield of the full tree:n" + sentenceNode.getTokens.mkString(" "))
println("nPrinting the yield of the VP node:n" + vpNode.getTokens.mkString(" "))
}

}
[/sourcecode]

There are a few things to note here. The import statement at the top is what tells Scala the fully qualified package names for the classes Node and Value. You could have equivalently written it less concisely as follows.

[sourcecode language=”scala”]
import opennlp.scalabha.model.Node
import opennlp.scalabha.model.Value
[/sourcecode]

Or, you could have left out the import statement and written the fully qualified names everywhere, e.g.:

[sourcecode language=”scala”]
val leaf1 = opennlp.scalabha.model.Value("I")
[/sourcecode]

Second, Node and Value are case classes. We’ll discus this more later, but for now, all you need to know is that to create an object of the Node or Value classes, it isn’t necessary to use the “new” keyword.

Third, the print statements are using the Scalabha API (Application Programming Interface) to do useful things with the objects, such as printing out the tree they describe, printing the yield of the nodes (the words that they cover), and so on. The scaladoc you looked at before for Scalabha shows you these functions, so go have a look if you haven’t already.

Note that if you had left the triggered compilation on, SBT will have automatically compiled the TreeTest.scala. Otherwise, make sure to compile it yourself. Then, run it.

[sourcecode language=”bash”]
$ scalabha run opennlp.bcomposes.TreeTest
Printing the full tree:
Node(S,List(Node(NP,List(Value(I))), Node(VP,List(Node(V,List(Value(like))), Node(NP,List(Value(coffee)))))))

Printing the children of the VP node:
List(Node(V,List(Value(like))), Node(NP,List(Value(coffee))))

Printing the yield of the full tree:
I like coffee

Printing the yield of the VP node:
like coffee
[/sourcecode]

Make and use your own package

By importing the classes you need in this manner, you can get more done by using them as you need. Any class in Scalabha or in the libraries that are included with it will be available for you, including any classes you define. As an example, do the following.

[sourcecode language=”bash”]
$ cd $SCALABHA_DIR/src/main/scala/opennlp/bcomposes
$ mkdir person
$ mkdir music
[/sourcecode]

Now save the Person class from the previous tutorial as Person.scala in the person directory. Here’s the code again (note the addition of the package statement).

[sourcecode language=”scala”]
package opennlp.bcomposes.person

class Person (
val firstName: String,
val lastName: String,
val age: Int,
val occupation: String
) {

def fullName: String = firstName + " " + lastName

def greet (formal: Boolean): String = {
if (formal)
"Hello, my name is " + fullName + ". I’m a " + occupation + "."
else
"Hi, I’m " + firstName + "!"
}

}
[/sourcecode]

Now save the following as RadioheadGreeting.scala in the music directory.

[sourcecode language=”scala”]
package opennlp.bcomposes.music

import opennlp.bcomposes.person.Person

object RadioheadGreeting {

def main (args: Array[String]) {
val thomYorke = new Person("Thom", "Yorke", 43, "musician")
val johnnyGreenwood = new Person("Johnny", "Greenwood", 39, "musician")
val colinGreenwood = new Person("Colin", "Greenwood", 41, "musician")
val edObrien = new Person("Ed", "O’Brien", 42, "musician")
val philSelway = new Person("Phil", "Selway", 44, "musician")
val radiohead = List(thomYorke, johnnyGreenwood, colinGreenwood, edObrien, philSelway)
radiohead.foreach(bandmember => println(bandmember.greet(false)))
}

}
[/sourcecode]

When we did the compilation tutorial previously, Person.scala and RadioheadGreeting.scala were in the same directory, which allowed the latter to know about the Person class. Now that they are in separate packages, the Person class must be explicitly imported; once you’ve done so, you can code with Person objects just as you did before.

Finally, to run it, we now must specify the fully qualified package name for RadioheadGreeting.

[sourcecode language=”bash”]
$ scalabha run opennlp.bcomposes.music.RadioheadGreeting
Hi, I’m Thom!
Hi, I’m Johnny!
Hi, I’m Colin!
Hi, I’m Ed!
Hi, I’m Phil!
[/sourcecode]

A note on package names and their relation to directories

Package names are made unique by certain conventions that generally ensure you won’t get clashes. For example, we are using opennlp.scalabha and opennlp.bcomposes, which I happen to know are unique. Quite often these names will include full internet domains, in reverse, like org.apache.commons and com.cloudera.crunch. By convention, we put the source files that are in packages (and subpackages) in directory structures that reflect the names. So, for example, opennlp.bcomposes.music.RadioheadGreeting is in the directory src/main/scala/opennlp/bcomposes/music. However, it is worth noting that this is not a hard constraint with Scala (as it is with Java).

There is a great deal more to using a build system, but this is where I must end this discussion, hoping it is enough to get the core concepts across and make it possible for my students to do the homework on part-of-speech tagging and making use of the opennlp.scalabha.postag package!

Copyright 2011 Jason Baldridge

The text of this tutorial is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License. Attribution may be provided by linking to www.jasonbaldridge.com and to this original tutorial.

Suggestions, improvements, extensions and bug fixes welcome — please email Jason at jasonbaldridge@gmail.com or provide a comment to this post.

 

First steps in Scala for beginning programmers, Part 10

Topics: scripting, compiling, main methods, return values of functions

Preface

This is part 10 of tutorials for first-time programmers getting into Scala. Other posts are on this blog, and you can get links to those and other resources on the links page of the Computational Linguistics course I’m creating these for.
The tutorials up to this point have been based on working with the Scala REPL or running basic scripts that are run from the command line. The latter is called “scripting” and usually is done for fairly simple, self-contained coding tasks. For more involved tasks that require a number of different modules and accessing libraries produced by others, it is necessary to work with a build system that brings together your code, others’ code, allows you to compile it, test it, and package it so that you can use it as an application.

This tutorial takes you from running Scala scripts to compiling Scala programs to create byte code that can be shared by different applications. This will act as a bridge to set you up for the next step of using a build system. Along the way, some points will be made about objects, extending on some of the ideas from the previous tutorial about object-oriented programming. At a high level, the relevance of objects to a larger, modularized code base should be pretty clear: objects encapsulate data and functions that can be used by other objects, and we need to be able to organize them so that objects know how to find other objects and class definitions. Build systems, which we’ll look at in the next tutorial, will make this straightforward.

Running Scala scripts

In the beginning, you started with the REPL.

[sourcecode language=”scala”]
scala> println("Hello, World!")
Hello, World!
[/sourcecode]

Of course, the REPL is just a (very useful) playground for trying out snippets of Scala code, not for doing real work. So, you saw that you could put code like println(“Hello, World!”) into a file called Hello.scala and run it from the command line.

[sourcecode language=”bash”]
$ scala Hello.scala
Hello, World!
[/sourcecode]

The homeworks and tutorials done so far have worked in this way, though they are a bit more complex. We can even include class definitions and objects created from a class. For example, using the Person class from the previous tutorial, we can put all the code into a file called People.scala (btw, this name doesn’t matter — could as well be Blurglecruncheon.scala).

[sourcecode language=”scala”]
class Person (
val firstName: String,
val lastName: String,
val age: Int,
val occupation: String
) {

def fullName: String = firstName + " " + lastName

def greet (formal: Boolean): String = {
if (formal)
"Hello, my name is " + fullName + ". I’m a " + occupation + "."
else
"Hi, I’m " + firstName + "!"
}

}

val johnSmith = new Person("John", "Smith", 37, "linguist")
val janeDoe = new Person("Jane", "Doe", 34, "computer scientist")
val johnDoe = new Person("John", "Doe", 43, "philosopher")
val johnBrown = new Person("John", "Brown", 28, "mathematician")

val people = List(johnSmith, janeDoe, johnDoe, johnBrown)
people.foreach(person => println(person.greet(true)))
[/sourcecode]

This can now be run from the command line, producing the expected result.

[sourcecode language=”bash”]
$ scala People.scala
Hello, my name is John Smith. I’m a linguist.
Hello, my name is Jane Doe. I’m a computer scientist.
Hello, my name is John Doe. I’m a philosopher.
Hello, my name is John Brown. I’m a mathematician.
[/sourcecode]

However, suppose you wanted to use the Person class from a different application (e.g. that is defined in a different file). You might think you could save the following in the file Radiohead.scala, and then run it with Scala.

[sourcecode language=”scala”]
val thomYorke = new Person("Thom", "Yorke", 43, "musician")
val johnnyGreenwood = new Person("Johnny", "Greenwood", 39, "musician")
val colinGreenwood = new Person("Colin", "Greenwood", 41, "musician")
val edObrien = new Person("Ed", "O’Brien", 42, "musician")
val philSelway = new Person("Phil", "Selway", 44, "musician")
val radiohead = List(thomYorke, johnnyGreenwood, colinGreenwood, edObrien, philSelway)
radiohead.foreach(bandmember => println(bandmember.greet(false)))
[/sourcecode]

However, if you do “scala Radiohead.scala” you’ll see five errors, each one complaining that the type Person wasn’t found. How could Radiohead.scala know about the Person class and where to find its definition? I’m not aware of a way to do this with scripting-style Scala programming, and even though I suspect there may be a way to do something this simple, I don’t even care to know it. Let’s just get straight to compiling.

Compiling

The usual thing we do with Scala is to compile our programs to byte code. We won’t go into the details of that, but it basically means that Scala turns the text of a Scala program into a compiled set of machine instructions that can be interpreted by your operating system. (It actually compiles to Java byte code, which is one reason it is pretty straightforward to use Java code when coding in Scala.)

So, what does compilation look like? We need to start by changing the code we did above a bit. Make a directory that has nothing in it, say /tmp/tutorial. Then save the following as PersonApp.scala in that directory.

[sourcecode language=”scala”]
class Person (
val firstName: String,
val lastName: String,
val age: Int,
val occupation: String
) {

def fullName: String = firstName + " " + lastName

def greet (formal: Boolean): String = {
if (formal)
"Hello, my name is " + fullName + ". I’m a " + occupation + "."
else
"Hi, I’m " + firstName + "!"
}

}

object PersonApp {

def main (args: Array[String]) {
val johnSmith = new Person("John", "Smith", 37, "linguist")
val janeDoe = new Person("Jane", "Doe", 34, "computer scientist")
val johnDoe = new Person("John", "Doe", 43, "philosopher")
val johnBrown = new Person("John", "Brown", 28, "mathematician")

val people = List(johnSmith, janeDoe, johnDoe, johnBrown)
people.foreach(person => println(person.greet(true)))
}

}
[/sourcecode]

Notice that the code looks pretty similar to the script above, but now we have a PersonApp object with a main method. The main method contains all the stuff that the original script had after the Person definition. Notice also that there is an args argument to the main method, which should look familiar now. What you are seeing is that a Scala script is basically just a simplified view of an object with a main method. Such scripts use the convention that the Array[String] provided to the method is called args.

Okay, so now consider what happens if you run “scala PersonApp.scala” — nothing at all. That’s because there is no executable code available outside of the object and class definitions. Instead, we need to compile the code and then run the main method of specific objects. The next step is to run scalac (N.B. “scalac” with a “c”, not “scala”) on PersonApp.scala. The name scalac is short for Scala compiler. Do the following steps in the /tmp/tutorial directory.

[sourcecode language=”bash”]
$ scalac PersonApp.scala
$ ls
Person.class                    PersonApp.class
PersonApp$$anonfun$main$1.class PersonApp.scala
PersonApp$.class
[/sourcecode]

Notice that a number of *.class files have been generated. These are byte code files that the scala application is able to run. A nice thing here is that it all the compilation is done: when in the past you ran “scala” on your programs (scripts), it had to first compile the instructions and then run the program. Now we are separating these steps into a compilation phase and a running phase.

Having generated the class files, we can run any object that has a main method, like PersonApp.

[sourcecode language=”bash”]
$ scala PersonApp
Hello, my name is John Smith. I’m a linguist.
Hello, my name is Jane Doe. I’m a computer scientist.
Hello, my name is John Doe. I’m a philosopher.
Hello, my name is John Brown. I’m a mathematician.
[/sourcecode]

Try running “scala Person” to see the error message it gives you.

Next, move the Radiohead.scala script that you saved earlier into this directory and run it.

[sourcecode language=”bash”]
$ scala Radiohead.scala
Hi, I’m Thom!
Hi, I’m Johnny!
Hi, I’m Colin!
Hi, I’m Ed!
Hi, I’m Phil!
[/sourcecode]

This is the same script, but now it is in a directory that contains the Person.class file, which tells Scala everything that Radiohead.scala needs to construct objects of the Person class. Scala makes available any class file that is defined in the CLASSPATH, an environment variable that by default includes the current working directory.

Despite this success, we’re going away from script land with this post, so change the contents of Radiohead.scala to be the following.

[sourcecode language=”scala”]
object RadioheadGreeting {

def main (args: Array[String]) {
val thomYorke = new Person("Thom", "Yorke", 43, "musician")
val johnnyGreenwood = new Person("Johnny", "Greenwood", 39, "musician")
val colinGreenwood = new Person("Colin", "Greenwood", 41, "musician")
val edObrien = new Person("Ed", "O’Brien", 42, "musician")
val philSelway = new Person("Phil", "Selway", 44, "musician")
val radiohead = List(thomYorke, johnnyGreenwood, colinGreenwood, edObrien, philSelway)
radiohead.foreach(bandmember => println(bandmember.greet(false)))
}

}
[/sourcecode]

Then run scalac on all of the *.scala files in the directory. There are now more class files, corresponding to the RadioheadGreeting object we defined.

[sourcecode language=”bash”]
$ scalac *.scala
$ ls
Person.class                            Radiohead.scala
PersonApp$$anonfun$main$1.class         RadioheadGreeting$$anonfun$main$1.class
PersonApp$.class                        RadioheadGreeting$.class
PersonApp.class                         RadioheadGreeting.class
PersonApp.scala
[/sourcecode]

You can now run “scala RadioheadGreeting” to get the greeting from the band members. Notice that the file RadioheadGreeting was saved in was called Radiohead.scala and that no class files were generated called Radiohead.class, etc. Again, the file name could have been named something entirely different, like Turlingdrome.scala. (Embrace your inner Vogon.)

Multiple objects in the same file

There is no problem having multiple objects with main methods in the same file. When you compile the file with scalac, each object generates its own set of class files, and you call scala on whichever class file contains the definition for the main method you want to run. As an example, save the following as Greetings.scala.

[sourcecode language=”scala”]
object Hello {
def main (args: Array[String]) {
println("Hello, world!")
}
}

object Goodbye {
def main (args: Array[String]) {
println("Goodbye, world!")
}
}

object SayIt {
def main (args: Array[String]) {
args.foreach(println)
}
}
[/sourcecode]

Next compile the file and then you can run any of the generated class files (since they all have main methods).

[sourcecode language=”bash”]
$ scalac Greetings.scala
$ scala Hello
Hello, world!
$ scala Goodbye
Goodbye, world!
$ scala Goodbye many useless arguments
Goodbye, world!
$ scala SayIt "Oh freddled gruntbuggly" "thy micturations are to me" "As plurdled gabbleblotchits on a lurgid bee."
Oh freddled gruntbuggly
thy micturations are to me
As plurdled gabbleblotchits on a lurgid bee.
[/sourcecode]

In case you missed it earlier, the args array is where the command line arguments go and you can thus make use of them (or not, as in the case of the Hello and Goodbye objects).

Functions with return values versus those without

Some functions return a value while others do not. As a simple example, consider the following pairs of functions.

[sourcecode language=”scala”]
scala> def plusOne (x: Int) = x+1
plusOne: (x: Int)Int

scala> def printPlusOne (x: Int) = println(x+1)
printPlusOne: (x: Int)Unit
[/sourcecode]

The first takes an Int argument and returns an Int, which is a value. The other takes an Int and returns Unit, which is to say it doesn’t return a value. Notice the difference in behavior between the two following uses of the functions.

[sourcecode language=”scala”]
scala> val foo = plusOne(2)
foo: Int = 3

scala> val bar = printPlusOne(2)
3
bar: Unit = ()
[/sourcecode]

Scala uses a slightly subtle distinction in function definitions that can distinguish functions that return values versus those that return Unit (no value): If you don’t use an equals sign in the definition, it means that the function returns Unit.

[sourcecode language=”scala”]
scala> def plusOneNoEquals (x: Int) { x+1 }
plusOneNoEquals: (x: Int)Unit

scala> def printPlusOneNoEquals (x: Int) { println(x+1) }
printPlusOneNoEquals: (x: Int)Unit
[/sourcecode]

Notice that the above definition of plusOneNoEquals returns Unit, even though it looks almost identical to plusOne defined earlier. Check it out.

[sourcecode language=”scala”]
scala> val foo = plusOneNoEquals(2)
foo: Unit = ()
[/sourcecode]

Now look back at the main methods given earlier. No equals. Yep, they don’t have a return value. They are the entry point into your code, and any effects of running the code must be output to the console (e.g. with println or via a GUI) or written to the file system (or the internet somewhere). The outputs of such functions (ones which do not return a value) are called side-effects. You need them for the main methods. However, in many styles of programming, a great deal of work is done with side-effects. I’ve been trying to gently lead the readers of this tutorial to adopt a more functional approach that tries to avoid them. I’ve found it a more effective style myself in my own coding, so I’m hoping it will serve you all better to start from that point. (Note that Scala supports many styles of programming, which is nice because you have choice and can go with what you find most suitable.)

Cleaning up

You may have noticed that the directory you are working in as you run scalac on your scala files becomes quite littered with class files. For example, here’s what the state of the code directory worked with in this tutorial looks like after compiling all files.

[sourcecode language=”bash”]
$ ls
Goodbye$.class                          PersonApp.scala
Goodbye.class                           Radiohead.scala
Greetings.scala                         RadioheadGreeting$$anonfun$main$1.class
Hello$.class                            RadioheadGreeting$.class
Hello.class                             RadioheadGreeting.class
Person.class                            SayIt$$anonfun$main$1.class
PersonApp$$anonfun$main$1.class         SayIt$.class
PersonApp$.class                        SayIt.class
PersonApp.class
[/sourcecode]

A mess, right? Generally, one would rarely develop a Scala application by compiling it directly in this way. Instead a build system is used to manage the compilation process, organize the files, and allow one to easily access software libraries created by other developers. The next tutorial will cover this, using SBT (the Simple Build Tool).

Copyright 2011 Jason Baldridge

The text of this tutorial is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License. Attribution may be provided by linking to www.jasonbaldridge.com and to this original tutorial.

Suggestions, improvements, extensions and bug fixes welcome — please email Jason at jasonbaldridge@gmail.com or provide a comment to this post.

First steps in Scala for beginning programmers, Part 9

Topics: objects, classes, inheritance, traits, Lists with multiple related types, apply

Preface

This is part 9 of tutorials for first-time programmers getting into Scala. Other posts are on this blog, and you can get links to those and other resources on the links page of the Computational Linguistics course I’m creating these for.

This tutorial is about object-oriented programming with Scala. Most of what we’ve seen so far has been programming with functions and using basic types, like Int, Double, and String, and with predefined types like List and Map. As it turns out, these are all classes, or types of Scala data structures that allow one to create objects, or instances of the type. This tutorial will not give a broad introduction to object-oriented programming, but it will give some practical examples of classes and objects and how to use them. I apologize in advance for some sloppiness in the presentation of object-oriented concepts; the intent is to get across the ideas for beginners mainly through intuitive examples without being mired in lots of technical details. See the Wikipedia page on object-oriented programming for more detail.

Note that the definitions of objects and classes in this tutorial are most easily viewed as plain text, out of the REPL. So, I’ll put a piece of code into the text, and you should add it to your own REPL (by simply cutting and pasting) in order to be able to follow along.

Objects

At its core, an object can be thought of as a structure that encapsulates some data and functions. Let’s start with an an example of an object representing a person and some of their possible attributes.

[sourcecode language=”scala”]
object JohnSmith {
val firstName = "John"
val lastName = "Smith"
val age = 37
val occupation = "linguist"

def fullName: String = firstName + " " + lastName

def greet (formal: Boolean): String = {
if (formal)
"Hello, my name is " + fullName + ". I’m a " + occupation + "."
else
"Hi, I’m " + firstName + "!"
}

}
[/sourcecode]

If you put this into the Scala REPL, you’ll be able to access the fields (firstName, lastName, age, and occupation) and the functions (fullName and greet).

[sourcecode language=”scala”]
scala> JohnSmith.firstName
res0: java.lang.String = John

scala> JohnSmith.fullName
res1: String = John Smith

scala> JohnSmith.greet(true)
res2: String = Hello, my name is John Smith. I’m a linguist.

scala> JohnSmith.greet(false)
res3: String = Hi, I’m John!
[/sourcecode]

So, at its most basic level, an object is just that: a collection of values and functions (also often called methods). You can access any of those values or functions by giving the name of the object followed by a period followed by the value or function you want to use. This can be useful for organizing such collections, but it also leads to many more possibilities, as we’ll see.

We might of course be interested in having the information about another person encapsulated in this way. We could do this by mimicking the definition for John Smith.

[sourcecode language=”scala”]
object JaneDoe {
val firstName = "Jane"
val lastName = "Doe"
val age = 34
val occupation = "computer scientist"

def fullName: String = firstName + " " + lastName

def greet (formal: Boolean): String = {
if (formal)
"Hello, my name is " + fullName + ". I’m a " + occupation + "."
else
"Hi, I’m " + firstName + "!"
}

}
[/sourcecode]

After adding the above code to the REPL, now Jane Doe can greet us.

[sourcecode language=”scala”]
scala> JaneDoe.greet(true)
res4: String = Hello, my name is Jane Doe. I’m a computer scientist.

scala> JaneDoe.greet(false)
res5: String = Hi, I’m Jane!
[/sourcecode]

Of course, I created the JaneDoe object by doing a copy-and-paste and then replacing the fields with Jane Doe’s information. This leads to a lot of wasted effort: the fields are the same, but the values are different, and the functions are completely identical. If you want to change something about the way greetings are made, you’d have to update it across all of the objects.

More importantly, these two objects are completely distinct from one another: one cannot put them in a list and map a function over that list. Consider the following failed attempt.

[sourcecode language=”scala”]
scala> val people = List(JohnSmith, JaneDoe)
people: List[ScalaObject] = List(JohnSmith$@698fcb66, JaneDoe$@5f72cbae)

scala> people.map(person => person.firstName)
<console>:11: error: value firstName is not a member of ScalaObject
people.map(person => person.firstName)
^
[/sourcecode]

The only thing that Scala knowns about JohnSmith and JaneDoe is that they are ScalaObjects. That means that a list of such objects can basically just contain them and allow you to move them around as a group. So, something more is needed to make these collections more useful and more general.

Classes

With the list above, what we’d like to have is a List[Person], where Person is a type that has known fields and functions. We can accomplish this by defining a Person class and then defining John and Jane as members of that class. This also reduces the cut-and-paste duplication problem noted earlier. Here’s what it looks like.

[sourcecode language=”scala”]
class Person (
val firstName: String,
val lastName: String,
val age: Int,
val occupation: String
) {

def fullName: String = firstName + " " + lastName

def greet (formal: Boolean): String = {
if (formal)
"Hello, my name is " + fullName + ". I’m a " + occupation + "."
else
"Hi, I’m " + firstName + "!"
}

}
[/sourcecode]

The class keyword indicates that this is a class definition and Person is the name of the class. The next part of the definition is a set of parameters to the class that allow us to construct objects that are instances of the class — in other words, they are placeholders that allow us to use the Person class as a factory for creating Person objects. We do this by using the new keyword, giving the name of the class and supplying the values for each of the parameters. For example, here’s how we can create John Smith now.

[sourcecode language=”scala”]
scala> val johnSmith = new Person("John", "Smith", 37, "linguist")
johnSmith: Person = Person@1979d4fb
[/sourcecode]

Just as we could with the one-off standalone JohnSmith object previously, we can now access the fields and functions.

[sourcecode language=”scala”]
scala> johnSmith.age
res8: Int = 37

scala> johnSmith.greet(true)
res9: String = Hello, my name is John Smith. I’m a linguist.
[/sourcecode]

Defining other people is now easy, and doesn’t require any cutting-and-pasting.

[sourcecode language=”scala”]
scala> val janeDoe = new Person("Jane", "Doe", 34, "computer scientist")
janeDoe: Person = Person@7ff5376c

scala> val johnDoe = new Person("John", "Doe", 43, "philosopher")
johnDoe: Person = Person@6544c984

scala> val johnBrown = new Person("John", "Brown", 28, "mathematician")
johnBrown: Person = Person@4076a247
[/sourcecode]

These Person objects can now be put into a list together, giving us a List[Person] that allows mapping to retrieve specific values, like first names and ages, and performing computations like calculating the average age of the individuals in the list.

[sourcecode language=”scala”]
scala> val people = List(johnSmith, janeDoe, johnDoe, johnBrown)
people: List[Person] = List(Person@1979d4fb, Person@7ff5376c, Person@6544c984, Person@4076a247)

scala> people.map(person => person.firstName)
res10: List[String] = List(John, Jane, John, John)

scala> people.map(person => person.age)
res11: List[Int] = List(37, 34, 43, 28)

scala> people.map(person => person.age).sum/people.length.toDouble
res12: Double = 35.5
[/sourcecode]

We can sort them according to age.

[sourcecode language=”scala”]
scala> val ageSortedPeople = people.sortBy(_.age)
ageSortedPeople: List[Person] = List(Person@4076a247, Person@7ff5376c, Person@1979d4fb, Person@6544c984)

scala> ageSortedPeople.map(person => person.fullName + ":" + person.age)
res13: List[java.lang.String] = List(John Brown:28, Jane Doe:34, John Smith:37, John Doe:43)
[/sourcecode]

We can also group people by first name, last name, etc.

[sourcecode language=”scala”]
scala> people.groupBy(person => person.firstName)
res14: scala.collection.immutable.Map[String,List[Person]] = Map(Jane -> List(Person@7ff5376c), John -> List(Person@1979d4fb, Person@6544c984, Person@4076a247))

scala> people.groupBy(person => person.lastName)
res15: scala.collection.immutable.Map[String,List[Person]] = Map(Brown -> List(Person@4076a247), Smith -> List(Person@1979d4fb), Doe -> List(Person@7ff5376c, Person@6544c984))
[/sourcecode]

With this, we can have all the Johns greet us.

[sourcecode language=”scala”]
scala> people.groupBy(person => person.firstName)("John").foreach(john => println(john.greet(true)))
Hello, my name is John Smith. I’m a linguist.
Hello, my name is John Doe. I’m a philosopher.
Hello, my name is John Brown. I’m a mathematician.
[/sourcecode]

Standalone objects

Above, we saw how to create instances of the Person class by using the new keyword and assigning the resulting object to a variable. We can come back full circle to the first JohnSmith object we created, which was a standalone ScalaObject. We can instead create such a standalone object by extending the Person class.

[sourcecode language=”scala”]
scala> object ThomYorke extends Person("Thom", "Yorke", 43, "musician")
defined module ThomYorke

scala> ThomYorke.greet(true)
res25: String = Hello, my name is Thom Yorke. I’m a musician.
[/sourcecode]

By extending the Person class to create the object, we are saying that the object is a kind of Person — see more on inheritance below. So, ThomYorke is a Person object, like the others we created, but it is for a different use case that we’ll see more of in the next tutorial. For now, I’ll summarize, very roughly, by saying that the ThomYorke object can be made more accessible by other code that might be using my code, while the johnSmith and janeDoe objects are going to be more locally contained.

Inheritance

The standalone objects lead us naturally to the idea of inheritance. In many domains, there are natural hierachies of types, such that properties of a super type are inherited by its subtypes (e.g. fish have gills and swim, so salmon have gills and swim). For example, we could have a Linguist type that is a kind of Person, a ComputerScientist type that is a kind of Person, and so on. To model this, we create one class that extends another and possibly provides some additional parameters, such as the following definition of a Linguist sub-type of Person.

[sourcecode language=”scala”]
class Linguist (
firstName: String,
lastName: String,
age: Int,
val speciality: String,
val favoriteLanguage: String
) extends Person(firstName, lastName, age, "linguist") {

def workGreeting =
"As a " + occupation + ", I am a " + speciality + " who likes to study the language " + favoriteLanguage + "."

}
[/sourcecode]

The Linguist class has its own parameter list: some of these, like firstName, lastName, and age, are passed on to Person, and there are new parameter fields speciality and favoriteLanguage. The extends portion of the definition passes on the relevant parameters needed to construct all the information to make a Person, and for a Linguist, it directly sets the occupation parameter to be “linguist” — thus, we don’t need to provide that when we construct a Linguist, such as Noam Chomsky.

[sourcecode language=”scala”]
scala> val noamChomsky = new Linguist("Noam", "Chomsky", 83, "syntactician", "English")noamChomsky: Linguist = Linguist@54c0627f
[/sourcecode]

Having defined a Linguist object in this way, we can ask it to give its work greeting.

[sourcecode language=”scala”]
scala> noamChomsky.workGreeting
res26: java.lang.String = As a linguist, I am a syntactician who likes to study the language English.
[/sourcecode]

We can also access fields and functions of Person objects, like age and greet.

[sourcecode language=”scala”]
scala> noamChomsky.age
res27: Int = 83

scala> noamChomsky.greet(true)
res28: String = Hello, my name is Noam Chomsky. I’m a linguist.
[/sourcecode]

Of course, the Linguist-specific fields like favoriteLanguage are accessible too.

[sourcecode language=”scala”]
scala> noamChomsky.favoriteLanguage
res29: String = English
[/sourcecode]

The observant reader will have noticed that some of the parameters are prefaced with val and others are not. We’ll get back to that distinction a bit later.

Traits

We could of course now go on to define a ComputerScientist class that would also have  workGreeting function, but the Linguist.workGreeting and ComputerScientist.workGreeting would be entirely separate. To enable this, we can use traits, which are like classes, but which define an interface of functions and fields that classes can supply concrete values and implementations for.  (Note: traits can also define concrete fields and functions, so they aren’t limited to placeholder functions as we show below.)

As an example, here’s a Worker trait, which simply defines a function workGreeting and declares that it must return a String.

[sourcecode language=”scala”]
trait Worker {
def workGreeting: String
}
[/sourcecode]

The Linguist class defined earlier already provides an implementation of that function. To allow a Linguist to be considered as a type of Worker, we add with Worker after extending Person.

[sourcecode language=”scala”]
class Linguist (
firstName: String,
lastName: String,
age: Int,
val speciality: String,
val favoriteLanguage: String
) extends Person(firstName, lastName, age, "linguist") with Worker {

def workGreeting =
"As a " + occupation + ", I am a " + speciality + " who likes to study the language " + favoriteLanguage + "."

}
[/sourcecode]

This is called “mixing in” the trait Worker, because the Linguist class mixes in the fields and functions of Worker with those of Person.

Note that we can also create classes that simply extend a trait like Worker.

[sourcecode language=”scala”]
class Student (school: String, subject: String) extends Worker {
def workGreeting = "I’m studying " + subject + " at " + school + "!"
}
[/sourcecode]

We can now create a Student object and request their greeting.

[sourcecode language=”scala”]
scala> val anonymousStudent = new Student("The University of Texas at Austin", "history")
anonymousStudent: Student = Student@734445b5

scala> anonymousStudent.workGreeting
res32: java.lang.String = I’m studying history at The University of Texas at Austin!
[/sourcecode]

Notice that the parameters school and subject were not preceded by val in the definition of Student. That means that they are not member fields of the Student class, which means that they cannot be accessed externally. For example, attempting to access the value provided for school for anonymousStudent fails.

[sourcecode language=”scala”]
scala> anonymousStudent.school
<console>:11: error: value school is not a member of Student
anonymousStudent.school
[/sourcecode]

Of course, internally, Student can use the values provided to such parameters, for example in defining the result of workGreeting. This sort of encapsulation hides properties of the objects of a class from code that is outside the class; this strategy can help reduce the degrees of freedom available to users of your code so that they only use what you want them to. In general, if others don’t need to use it, you shouldn’t make it available to them.

Returning to classes that are both Persons and Workers, when we define a ComputerScientist, we do a similar extends … with declaration as we did for Linguist.

[sourcecode language=”scala”]
class ComputerScientist (
firstName: String,
lastName: String,
age: Int,
val speciality: String,
favoriteProgrammingLanguage: String
) extends Person(firstName, lastName, age, "computer scientist") with Worker {

def workGreeting =
"As a " + occupation + ", I work on " + speciality + ". Much of my code is written in " + favoriteProgrammingLanguage + "."

}
[/sourcecode]

Let’s create Andrew McCallum as a ComputerScientist object.

[sourcecode language=”scala”]
scala> val andrewMcCallum = new ComputerScientist("Andrew", "McCallum", 44, "machine learning", "Scala")
andrewMcCallum: ComputerScientist = ComputerScientist@493cd5ba

scala> andrewMcCallum.workGreeting
res31: java.lang.String = As a computer scientist, I work on machine learning. Much of my code is written in Scala.
[/sourcecode]

Because we redefined Linguist to be a Worker, we need to recreate Noam Chomsky using the new definition. (The creation looks the same as before, but it uses the new class definition that has been updated in the REPL.)

[sourcecode language=”scala”]
scala> val noamChomsky = new Linguist("Noam", "Chomsky", 83, "syntactician", "English")
noamChomsky: Linguist = Linguist@6fccaf14
[/sourcecode]

A minor thing to note: the speciality field of ComputerScientist is disconnected from that of Linguist, so there is no particular expectation of consistency of use across the two: for Linguist it is a description of a person working in a sub-area but for ComputerScientist is a description of a sub-area.

So, what happens if we put noamChomsky and andrewMcCallum in a List together?

[sourcecode language=”scala”]
scala> val professors = List(noamChomsky, andrewMcCallum)
professors: List[Person with Worker] = List(Linguist@6fccaf14, ComputerScientist@493cd5ba)
[/sourcecode]

Scala has created a list with type List[Person with Worker]; this is the most specific type that is valid for all elements of the list. It means we can treat all of the elements as Persons, e.g. accessing their occupation (which is a member field of Person).

[sourcecode language=”scala”]
scala> professors.map(prof => prof.occupation)
res34: List[String] = List(linguist, computer scientist)
[/sourcecode]

And we can treat each element of the list as a Person and a Worker, e.g. printing out their fullName (from Person) and their workGreeting (from Worker).

[sourcecode language=”scala”]
scala> professors.foreach(prof => println(prof.fullName + ": " + prof.workGreeting))
Noam Chomsky: As a linguist, I am a syntactician who likes to study the language English.
Andrew McCallum: As a computer scientist, I work on machine learning. Much of my code is written in Scala.
[/sourcecode]

We cannot, however, access fields and functions that are specific to Linguists or ComputerScientists, such as favoriteLanguage from Linguist.

[sourcecode language=”scala”]
scala> professors.map(prof => prof.favoriteLanguage)
<console>:15: error: value favoriteLanguage is not a member of Person with Worker
professors.map(prof => prof.favoriteLanguage)
[/sourcecode]

It is easy to see why Scala has this behavior: even though that would have been valid for noamChomsky, it would not be for andrewMcCallum (according to the way we defined Linguist and ComputerScientist).

Matching on types in polymorphic Lists

Consider what happens when the anonymousStudent is in a list with the professors.

[sourcecode language=”scala”]
scala> val workers = List(noamChomsky, andrewMcCallum, anonymousStudent)
workers: List[ScalaObject with Worker] = List(Linguist@6fccaf14, ComputerScientist@493cd5ba, Student@734445b5)
[/sourcecode]

The Person type is gone, and we now have a list of a more general type ScalaObject with Worker. Now we can only use the workGreeting method from Worker.

However, it is worth pointing out that match statements come in handy when you have collections of heterogenous objects. For example, put the following code into the REPL.

[sourcecode language=”scala”]
val people = List(johnSmith, noamChomsky, andrewMcCallum, anonymousStudent)

people.foreach { person =>
person match {
case x: Person with Worker => println(x.fullName + ": " + x.workGreeting)
case x: Person => println(x.fullName + ": " + x.greet(true))
case x: Worker => println("Anonymous:" + x.workGreeting)
}
}
[/sourcecode]

The result is the following (remember that johnSmith was never defined as a Linguist — he was defined as a Person whose occupation is “linguist”).

[sourcecode language=”scala”]
John Smith: Hello, my name is John Smith. I’m a linguist.
Noam Chomsky: As a linguist, I am a syntactician who likes to study the language English.
Andrew McCallum: As a computer scientist, I work on machine learning. Much of my code is written in Scala.
Anonymous:I’m studying history at The University of Texas at Austin!
[/sourcecode]

So, we can switch our behavior by matching to more specific types using Scala’s pattern matching.

The apply function

Scala provides a simple but incredibly nice feature: if you define an apply function in a class or object, you don’t actually need to write “apply” in order to use it. As an example, the following object adds one to an argument supplied to its apply method.

[sourcecode language=”scala”]
object AddOne {
def apply (x: Int): Int = x+1
}
[/sourcecode]

So, we can use it just like you’d normally expect.

[sourcecode language=”scala”]
scala> AddOne.apply(3)
res41: Int = 4
[/sourcecode]

But, we can also do without the “.apply” portion and get the same result.

[sourcecode language=”scala”]
scala> AddOne(3)
res42: Int = 4
[/sourcecode]

If a class has an apply method, then we can do the same trick with any object of that class.

[sourcecode language=”scala”]
class AddN (amountToAdd: Int) {
def apply (x: Int): Int = x + amountToAdd
}

scala> val add2 = new AddN(2)
add2: AddN = AddN@43ca04a1

scala> add2(5)
res43: Int = 7

scala> val add42 = new AddN(42)
add42: AddN = AddN@83e591f

scala> add42(8)
res44: Int = 50
[/sourcecode]

As it turns out, you’ve been using apply methods quite often, without knowing it! When you have a List and you access an element by index, you’ve used the apply method of the List class.

[sourcecode language=”scala”]
scala> val numbers = 10 to 20 toList
numbers: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)

scala> numbers(3)
res46: Int = 13

scala> numbers.apply(3)
res47: Int = 13
[/sourcecode]

Same thing for accessing values using keys in a Map, and similarly for many other of the classes you’ve been using in Scala so far.

Wrap-up

This tutorial has covered the basics of object-oriented programming in Scala. Hopefully, it is enough to give a decent sense of what objects and classes are and how you can do things with them. There is much much more to be learned about them, but this should be sufficient to get you started so that further study can be done meaningfully. It is important to understand these concepts since Scala is object-oriented from the ground up. In fact, in many of the previous tutorials, I’ve at times gone through some extra hoops to try to describe what is going on without having to talk about object-orientation. But now you can see things like Int, Double, List, Map, and so on for what they are: classes that contain particular fields and functions that you can use to get things done. You can now start coding your own classes to enable your own custom behaviors in your applications.

Copyright 2011 Jason Baldridge

The text of this tutorial is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License. Attribution may be provided by linking to www.jasonbaldridge.com and to this original tutorial.

Suggestions, improvements, extensions and bug fixes welcome — please email Jason at jasonbaldridge@gmail.com or provide a comment to this post.

Belle Scarlett Baldridge

In loving memory of Belle Scarlett Baldridge
September 29, 2011

I buried my baby daughter Belle today. It wasn’t supposed to be this way. Babies just aren’t supposed to die. We are fortunate to live in a time of favorable survival rates for babies and their mothers. We enjoy high degrees of order and predictability in our day-to-day lives (here in the USA, at least), and it is easy to forget that one still has innocence to lose. This has been the saddest, hardest week of my life. I had always heard that a parent should never have to bury their own child. I didn’t doubt it, but now I know it, fully. This morning, I gazed down at a gaping hole, my little girl’s grave, while I held her casket in my arms. It mirrored the hole already in my heart. It disarmed and terrified me, but also showed me that both were there to receive Belle and preserve her memory.

With this post, I seek to honor and remember Belle, to thank those who have supported us this week, to help myself grieve, and hopefully, to help—perhaps a little—others in the future who must unfortunately deal with the death of their child. My apologies if the post is on the (melo)dramatic side. It’s how I feel, and it seems to be part of my healing process, so please bear with me.

My wife Cheryl and I had long been anticipating Belle’s arrival, with a due date of today – October 4, 2011. Like most expecting parents, we had considered many of the possible outcomes of the pregnancy, including even the possibility of complications that would involve our baby and/or Cheryl needing hospitalization — but never the possibility that our baby Belle wouldn’t make it into this world, never the possibility of a stillbirth. The unyielding march of life and death has left us suddenly and unexpectedly bereft of a person we loved, cared for and were ready to teach and eventually send forth into the world.

We knew Belle from her kicks, and her responses to our voices, songs, and laughter. It’s an imperfect medium of communication, but it suffices to start the relationship that one builds with one’s child — they simply aren’t strangers when you see them for the first time. This is something that can perhaps be hard to understand for those who have not yet had children, and it is a common source of pain for parents of stillborn children: it is somehow perceived by many to not be as great a loss as for those whose children died after their birth date. A great line I read in one of the many materials I’ve been given about such loss is that on a scale of one to ten, the pain of losing a child is always a ten, no matter the age or circumstances. It’s true. I would submit that there is a further dynamic element for parents of a stillborn child: you have gone from a state of accelerating excitement and anticipation, to a huge resounding thud of shock and disbelief. The “what if’s” have in very short order become “never be’s.” This sudden reversal kicks in the first moment you are told that your baby’s heartbeat has stopped and then reverberates as you reel from the pain and try to regroup.

Little Belle is true to her name: she is beautiful, even in death. I can now only imagine what she would have looked like as she grew up, but thankfully I can do at least that. And, I can do that from a starting point of having been able to spend time with her on the day she was born, September 29, 2011. We had a wonderful team with us at Belle’s birth—including doctors, midwives, nurses, and doula—and they helped us through the intensely emotional and difficult process of bringing Belle into the world and, perhaps more importantly, to help us spend meaningful time with her before saying goodbye. They encouraged us to be with Belle, to hold her and take pictures, and not rush things. We now have at least those memories—even so bittersweet—to keep with us, something which many parents of stillborn babies are never given because no one tells them they can and should. This is a really important aspect of Belle’s birth that I hope to get across: you are hurting and spinning from the shock and pain, yet there are important decisions to be made from the very start; while you may have been provided with comprehensive and well-written literature on how to approach the situation, you have little emotional space for it and there is too much of it to possibly work through before you must make decisions.  If you or someone you care for finds themselves unfortunately in this situation, try to get across this message: take time with the baby and take pictures. You won’t get more chances later, and you’ll almost surely regret it if you don’t.

Another important thing for us was to have a small memorial service for Belle, and also a burial. As an agnostic without any religious affiliation, I had no default expectation for what to do. Cheryl and I had years ago decided that cremation would be the thing for us eventually. However, with Belle, Cheryl quickly realized that she wanted a place to visit her, so we went with a burial. I did not feel strongly about it, but it felt right to me when we did it today, so I’ll probably be glad for that choice in the long run. It was very hard to pick out her plot at the cemetery on Friday—it’s an area reserved for infants, a grid of small plots that serves as a concrete reminder of the fragility of the early days of life. Looking at the empty spot where Belle would be buried made it all seem more real, more this-is-really-happening, in the mix of surreal feelings of that day and the previous day. Of course, handing over a credit card to pay for the services and the plot then felt bizarre, an odd juxtaposition of a completely mundane action with the profound grief I was keeping in check. Regardless of that strangeness, it is one of those things which just must be done. Belle is now there, and it is a peaceful place, with trees and birds singing in them.

It turns out that stillbirths are more common than I would have ever thought. I had only directly known of one before Belle, and had assumed it must have been a case of extreme misfortune. Actually, in the USA, the average rate of stillbirths is roughly 1 in 150 births, about 26,000 babies every year. The rate is much higher in developing countries. Despite this prevalence, there apparently is not a great deal of research into it (and it seems to be an inherently difficult thing to research), so we still know little about specific actions that can be taken to prevent it. For the things we do know, such as tangled umbilical cords, there is very little warning — there is a window of perhaps 5-10 minutes from the time of fetal distress in which to save the baby. Knowing this actually relieved us of a great deal of guilt as we had initially second guessed ourselves, retracing our steps in the days leading up to Belle’s birth and imagining ways we could/should have known to try to get her out earlier.

Regardless of the statistics, regardless of whether we’ll know the cause of Belle’s death, it all just ends up feeling unfair. I’ve been robbed of my little girl, whose heart I had heard beating just days before. Belle should have had her fair shot at life, and I’m sure she would have made hers a great one. It shouldn’t have been this way, but that is what happened and now we must live with that and move on. In this, I’m so thankful for the amazing relationship I have with Cheryl. We’re both hurting, immensely, but we also are optimists who have both already overcome our fair share of challenges in our lives. Together, and with the help of family and friends, we’ll regroup and carry on, carrying Belle’s memory with us.

Little Belle, I’ll love you forever.

Addendum

There are many people who have provided us with amazing, and often unexpected, support over the last week.

Our doula, Shelley Scotka, was our shining light on the day of Belle’s birth. Many people have probably never heard of doulas — summarizing quickly, they are amazing women who assist in natural childbirth. They bring their knowledge of traditional birthing techniques and practical experience from many births to bear on yours, including translating what the doctors are saying and doing so that you hear what is going on, in simple, understandable terms. Shelley was there for our son’s delivery, a 50+ hour marathon that she did a great deal to ease. Little did we know that she would be every bit as vital for us for a stillbirth as she was for a live birth. She was a rock who helped before, during and after the delivery, and who continues to shower us with love and care.

We’re also incredibly thankful for the medical team that delivered Belle last Thursday at St. David’s North Austin. Our practice is OB-GYN North, and the midwives, doctor, and technician who had to tell us that Belle’s heartbeat had stopped were caring and kind, and helped us immensely with the initial shock and disbelief. Kathy Harrison-Short, CNM  had caught our son two years before and she immediately came to comfort us. Lisa Carlile, CNM stayed past her shift and was the one who ultimately caught Belle, at Cheryl’s request. Dr. Martha Smitz was the physician on duty that day. She demonstrated tremendous sensitivity, compassion and overwhelming competence throughout. She had an uncanny ability to put us at ease even in the midst of the sorrow and confusion we were going through. The nurses, other doctors, social worker and pastor were all similarly supportive and sensitive. The nurses deserve special thanks for taking such great care of Cheryl before the delivery and of Belle after it. Everyone treated us, and Belle, with tremendous dignity.

Since that day, our family, friends and colleagues have been incredibly supportive. One of the blessings in tragedy is the concrete realization that one is surrounded by a wonderful support network. My younger brother lives here in Austin and my mother had just arrived, ready to help us with Belle; they’ve been helping us through the whole thing, especially with our toddler son, even while dealing with their own loss and grief. My father flew in from Chicago, and my older brother immediately came over from Baton Rouge with his daughter. The sound of her playing with our toddler son over the weekend was a welcome, joyful addition that helped combat the otherwise tendency toward a somber mood. My brother’s wife helped us a great deal from afar, providing support both as a family member and as a practicing physician. My step-father will be here soon, a delayed visit (at my request) since I knew we’d need more backup once the main family contingent was gone.

Other have also given us great strength, including sharing their own pain and anger at the situation, and in a few cases, their own direct experience with stillbirths. There have been generous offers of help, including offers to teach some of my classes in the coming weeks. Though I’ve so far responded to almost none of them, I’ve read and appreciated every email of support from friends, colleagues, and students. In a way, this post is my response, so please consider this my thank you to you all. And to those who I have not yet gotten in touch with about Belle’s death, please understand that there has not been any particular plan or care with my communications regarding it — I’m just now getting geared up to pass the word on to more friends, and some of you are probably seeing this post as a result of that effort.

I must also give high praise to the people at Cook-Walden funeral homes. They have treated us very kindly and have been incredibly responsive to our needs. One of the things about the situation is that many decisions must be made in rapid succession, and you get some of them not-quite-right the first time around. Cook-Walden was very accommodating to changes in how we wanted to do the service and burial and to requests for articles of Belle’s that we only realized later that we’d want (such as a lock of her hair). They treated us and Belle with dignity and allowed us time and space to make decisions and say goodbye to her.

Finally, I must thank the volunteers from Now I Lay Me Down To Sleep, who Shelley called in for us. NILMDTS is a non-profit that has professional photographers who come to take pictures of stillborn babies and their families, and then later retouch them to provide nicer images of the baby than one could generally hope to capture by oneself. They were caring and professional, and we look forward to seeing the result of their work with Belle. If you are looking for a great non-profit to donate to, please consider them.