In Edward Scissorhands, there’s a castle on a hill that is, well, a little different. In a bygone era, the castle was a strange and enchanting place but is now showing signs of age and neglect. Broken windows let the weather in, and the rooms aren’t all what they once were. The house that once felt so comfortable to its inhabitants is now cold and uninviting. The object-oriented paradigm, too, is showing some signs of age, especially the earlier object-oriented implementations. The Java language, with its dated implementations of static typing and concurrency, needs a face-lift. In this section, we’re going to talk primarily about Scala in the context of that house on the hill, the object-oriented programming paradigm.
Scala runs on the Java virtual machine (JVM). I’m not going to offer an exhaustive overview of the Java language; that information is freely available elsewhere. You’ll see some Java ideas creeping through to Scala, but I’ll try to minimize their impact so you won’t have to learn two languages at once. For now, install Scala. I’m using version 2.7.7.final for this book.
When you have Scala working, fire up the console with the command scala. If all is well, you won’t get any error messages, and you will see a scala> prompt. You can then type a little code.
scala> println("Hello, surreal world")
|
|
Hello, surreal world
|
|
|
|
scala> 1 + 1
|
|
res8: Int = 2
|
|
|
|
scala> (1).+(1)
|
|
res9: Int = 2
|
|
|
|
scala> 5 + 4 * 3
|
|
res10: Int = 17
|
|
|
|
scala> 5.+(4.*(3))
|
|
res11: Double = 17.0
|
|
|
|
scala> (5).+((4).*(3))
|
|
res12: Int = 17
|
So, integers are objects. In Java, I’ve pulled out my fair share of hair converting between Int (primitives) and Integer (objects). In fact, everything is an object in Scala, with some small exceptions. That’s a significant departure from most statically typed object oriented languages. Let’s see how Scala handles strings:
scala> "abc".size
|
|
res13: Int = 3
|
So a string, too, is a first-class object, with a little syntactic sugar mixed in. Let’s try to force a type collision:
scala> "abc" + 4
|
|
res14: java.lang.String = abc4
|
|
|
|
scala> 4 + "abc"
|
|
res15: java.lang.String = 4abc
|
|
|
|
scala> 4 + "1.0"
|
|
res16: java.lang.String = 41.0
|
Hm...that’s not quite what we were looking for. Scala is coercing those integers into strings. Let’s try a little harder to force a mismatch:
scala> 4 * "abc"
|
|
<console>:5: error: overloaded method value * with alternatives (Double)Double
|
|
<and> (Float)Float <and> (Long)Long <and> (Int)Int <and> (Char)Int
|
|
<and> (Short)Int <and> (Byte)Int cannot be applied to (java.lang.String)
|
|
4 * "abc"
|
|
^
|
Ah. That’s the ticket. Scala is actually strongly typed. Scala will use type inference, so most of the time, it will understand the types of variables through syntactical clues, but unlike Ruby, Scala can do that type checking at compile time. Scala’s console actually compiles lines of code and runs each one piecemeal.
On a side note, I know you’re getting back Java strings. Most Scala articles and books go into this topic in more detail, but we can’t do so and still dive into the programming constructs that I think will be most interesting to you. I’ll point you to a few books that will go into the Java integration in detail. For now, I’m going to tell you that in many places, Scala has a strategy for managing types across two languages. Part of that is using simple Java types where they make sense, like java.lang.String. Please trust me, and accept these oversimplifications.
Now we’re going to move through some basic syntax quickly and strictly by example. Here are a few Scala true/false expressions:
scala> 5 < 6
|
|
res27: Boolean = true
|
|
|
|
scala> 5 <= 6
|
|
res28: Boolean = true
|
|
|
|
scala> 5 <= 2
|
|
res29: Boolean = false
|
|
|
|
scala> 5 >= 2
|
|
res30: Boolean = true
|
|
|
|
scala> 5 != 2
|
|
res31: Boolean = true
|
There’s nothing too interesting going on there. This is the C-style syntax that you’re familiar with from several of the languages we’ve talked about so far. Let’s use an expression in an if statement:
scala> val a = 1
|
|
a: Int = 1
|
|
|
|
scala> val b = 2
|
|
b: Int = 2
|
|
|
|
scala> if ( b < a) {
|
|
| println("true")
|
|
| } else {
|
|
| println("false")
|
|
| }
|
|
false
|
We assign a couple of variables and compare them in an if/else statement. Take a closer look at the variable assignment. First, notice that you didn’t specify a type. Unlike Ruby, Scala binds types at compile time. But unlike Java, Scala can infer the type, so you don’t have to type val a : Int = 1, though you can if you want.
Next, notice that these Scala variable declarations start with the val keyword. You can also use the var keyword. val is immutable; var is not. We’ll talk more about this later.
In Ruby, 0 evaluated to true. In C, 0 was false. In both languages, nil evaluated to false. Let’s see how Scala handles them:
scala> Nil
|
|
res3: Nil.type = List()
|
|
scala> if(0) {println("true")}
|
|
<console>:5: error: type mismatch;
|
|
found : Int(0)
|
|
required: Boolean
|
|
if(0) {println("true")}
|
|
^
|
|
scala> if(Nil) {println("true")}
|
|
<console>:5: error: type mismatch;
|
|
found : Nil.type (with underlying type object Nil)
|
|
required: Boolean
|
|
if(Nil) {println("true")}
|
|
^
|
So, a Nil is an empty list, and you can’t even test Nil or 0. This behavior is consistent with Scala’s strong, static typing philosophy. Nils and numbers are not booleans, so don’t treat them like booleans. With simple expressions and the most basic decision construct behind us, let’s move on to loops.
As the
next couple of programs get more complex, we’re going to run them as scripts. Like Ruby and Io, you’ll run them with scala path/to/program.scala.
You’ll see a number of ways to iterate over result sets in day 2 when we attack code blocks. For now, we’ll focus on the imperative programming style of loops. You’ll see that these look a lot like Java-style loop structures.
First is the basic while loop:
| scala/while.scala | |
def whileLoop {
|
|
var i = 1
|
|
while(i <= 3) {
|
|
println(i)
|
|
i += 1
|
|
}
|
|
}
|
|
whileLoop
|
|
We define a function. As a side note, Java developers will notice that you don’t have to specify public. In Scala, public is the default visibility, meaning this function will be visible to all.
Within the method, we declare a simple while loop that counts to three. i changes, so we declare it with var. Then, you see a Java-style declaration of a while statement. As you can see, the code inside braces executes unless the condition is false. You can run the code like this:
batate$ scala code/scala/while.scala
|
|
1
|
|
2
|
|
3
|
The for loop works a lot like Java’s and C’s but with a slightly different syntax:
| scala/for_loop.scala | |
def forLoop {
|
|
println( "for loop using Java-style iteration" )
|
|
for(i <- 0 until args.length) {
|
|
println(args(i))
|
|
}
|
|
}
|
|
|
|
forLoop
|
|
The argument is a variable, followed by the <- operator, followed by a range for the loop in the form of initialValue until endingValue. In this case, we’re iterating over the incoming command-line arguments:
batate$ scala code/scala/forLoop.scala its all in the grind
|
|
for loop using Java-style iteration
|
|
its
|
|
all
|
|
in
|
|
the
|
|
grind
|
As with Ruby, you can also use loops to iterate over a collection. For now, we’ll start with foreach, which is reminiscent of Ruby’s each:
| scala/ruby_for_loop.scala | |
def rubyStyleForLoop {
|
|
println( "for loop using Ruby-style iteration" )
|
|
args.foreach { arg =>
|
|
println(arg)
|
|
}
|
|
}
|
|
|
|
rubyStyleForLoop
|
|
args is a list with the inbound command-line arguments. Scala passes each element into this block, one by one. In our case, arg is one argument from the inbound args list. In Ruby, the same code would be args.each {|arg| println(arg) }. The syntax for specifying each argument is slightly different, but the idea is the same. Here’s the code in action:
batate$ scala code/scala/ruby_for_loop.scala freeze those knees chickadees
|
|
for loop using Ruby-style iteration
|
|
freeze
|
|
those
|
|
knees
|
|
chickadees
|
Later, you’ll find yourself using this method of iteration much more often than the other imperative loops. But since we’re concentrating on the house on the hill, we’ll delay that part of the conversation for a little while.
Like Ruby, Scala supports first-class ranges. Start the console, and enter these code snippets:
scala> val range = 0 until 10
|
|
range: Range = Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
|
|
|
|
scala> range.start
|
|
res2: Int = 0
|
|
|
|
scala> range.end
|
|
res3: Int = 10
|
That all makes sense. It works like Ruby’s range. You can also specify increments:
scala> range.step
|
|
res4: Int = 1
|
|
|
|
scala> (0 to 10) by 5
|
|
res6: Range = Range(0, 5, 10)
|
|
|
|
scala> (0 to 10) by 6
|
|
res7: Range = Range(0, 6)
|
The equivalent of Ruby’s range, 1..10, is 1 to 10, and the equivalent of Ruby’s range, 1...10, is 1 until 10. to is inclusive:
scala> (0 until 10 by 5)
|
|
res0: Range = Range(0, 5)
|
You can also specify direction with this:
scala> val range = (10 until 0) by -1
|
|
range: Range = Range(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
|
But the direction is not inferred:
scala> val range = (10 until 0)
|
|
range: Range = Range()
|
|
|
|
scala> val range = (0 to 10)
|
|
range: Range.Inclusive = Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
1 is the default step, regardless of the endpoints that you express for your range. You are not limited to integers:
scala> val range = 'a' to 'e'
|
|
range: RandomAccessSeq.Projection[Char] = RandomAccessSeq.Projection(a, b, c, d, e)
|
Scala will do some implicit type conversions for you. In fact, when you specified a for statement, you were actually specifying a range.
Like Prolog, Scala offers tuples. A tuple is a fixed-length set of objects. You’ll find this pattern in many other functional languages as well. The objects in a tuple can all have different types. In purely functional languages, programmers often express objects and their attributes with tuples. Try this example:
scala> val person = ("Elvis", "Presley")
|
|
person: (java.lang.String, java.lang.String) = (Elvis,Presley)
|
|
|
|
scala> person._1
|
|
res9: java.lang.String = Elvis
|
|
|
|
scala> person._2
|
|
res10: java.lang.String = Presley
|
|
|
|
scala> person._3
|
|
<console>:6: error: value _3 is not a member of (java.lang.String, java.lang.String)
|
|
person._3
|
|
^
|
Scala uses tuples rather than lists to do multivalue assignments:
scala> val (x, y) = (1, 2)
|
|
x: Int = 1
|
|
y: Int = 2
|
Since tuples have a fixed length, Scala can do static type checking based on each of the tuple values:
scala> val (a, b) = (1, 2, 3)
|
|
<console>:15: error: constructor cannot be instantiated to expected type;
|
|
found : (T1, T2)
|
|
required: (Int, Int, Int)
|
|
val (a, b) = (1, 2, 3)
|
|
^
|
|
<console>:15: error: recursive value x$1 needs type
|
|
val (a, b) = (1, 2, 3)
|
|
^
|
With these foundations out of the way, let’s put it all together. We’ll create some object-oriented class definitions.
The simplest classes, those with attributes but no methods or constructors, are simple, one-line definitions in Scala:
class Person(firstName: String, lastName: String)
|
You don’t have to specify any body to specify a simple value class. The Person class will be public and have firstName and lastName attributes. And you can use that class in the console:
scala> class Person(firstName: String, lastName: String)
|
|
defined class Person
|
|
scala> val gump = new Person("Forrest", "Gump")
|
|
gump: Person = Person@7c6d75b6
|
But you’re looking for a little more. Object-oriented classes mix data and behavior. Let’s build a full object-oriented class in Scala. We’ll call this class Compass. The compass orientation will start with north. We’ll tell the compass to turn 90 degrees left or right and update the direction accordingly. Here’s what the Scala code looks like, in its entirety:
| scala/compass.scala | |
class Compass {
|
|
val directions = List("north", "east", "south", "west")
|
|
var bearing = 0
|
|
print("Initial bearing: ")
|
|
println(direction)
|
|
def direction() = directions(bearing)
|
|
|
|
def inform(turnDirection: String) {
|
|
println("Turning " + turnDirection + ". Now bearing " + direction)
|
|
}
|
|
|
|
def turnRight() {
|
|
bearing = (bearing + 1) % directions.size
|
|
inform("right")
|
|
}
|
|
|
|
def turnLeft() {
|
|
bearing = (bearing + (directions.size - 1)) % directions.size
|
|
inform("left")
|
|
}
|
|
}
|
|
|
|
val myCompass = new Compass
|
|
|
|
myCompass.turnRight
|
|
myCompass.turnRight
|
|
|
|
myCompass.turnLeft
|
|
myCompass.turnLeft
|
|
myCompass.turnLeft
|
|
The syntax is relatively straightforward, with a couple of notable peculiarities. The constructor is responsible for defining instance variables (at least, those you don’t pass into the constructor) and methods. Unlike Ruby, all method definitions have parameter types and names. And the initial block of code isn’t in any method definition at all. Let’s take it apart:
class Compass {
|
|
val directions = List("north", "east", "south", "west")
|
|
var bearing = 0
|
|
print("Initial bearing: ")
|
|
println(direction)
|
The whole block of code following the class definition is actually the constructor. Our constructor has a List of directions and a bearing, which is simply an index for the directions. Later, turning will manipulate the bearing. Next, there are a couple of convenience methods to show the user of the class the current direction, in English:
def direction() = directions(bearing)
|
|
|
|
def inform(turnDirection: String) {
|
|
println("Turning " + turnDirection + ". Now bearing " + direction)
|
|
}
|
The constructor continues with method definitions. The direction method just returns the element of directions at the index of bearing. Scala conveniently allows an alternate syntax for one-line methods, omitting the braces around the method body.
The inform method prints a friendly message whenever the user turns. It takes a simple parameter, the direction of the turn. This method doesn’t return a value. Let’s look at the methods to handle turns.
def turnRight() {
|
|
bearing = (bearing + 1) % directions.size
|
|
inform("right")
|
|
}
|
|
|
|
def turnLeft() {
|
|
bearing = (bearing + (directions.size - 1)) % directions.size
|
|
inform("left")
|
|
}
|
The turns method changes the bearing based on the direction of the turn. The % operator is modular division. (This operator does a division operation, discarding the quotient and returning only the remainder.) The result is that right turns add one to the bearing and left turns subtract one, wrapping the result accordingly.
You’ve seen how the basic constructor works. It’s a code block that initializes classes and methods. You can have alternate constructors as well. Consider this Person class, with two constructors:
| scala/constructor.scala | |
class Person(firstName: String) {
|
|
println("Outer constructor")
|
|
def this(firstName: String, lastName: String) {
|
|
this(firstName)
|
|
println("Inner constructor")
|
|
}
|
|
def talk() = println("Hi")
|
|
}
|
|
val bob = new Person("Bob")
|
|
val bobTate = new Person("Bob", "Tate")
|
|
The class has a constructor with one parameter, firstName, and a method called talk. Notice the this method. That’s the second constructor. It takes two parameters, firstName and lastName. Initially, the method invokes this with the primary constructor, with only the firstName parameter.
The code after the class definition instantiates a person in two ways, first with the primary constructor, then next with the auxiliary constructor:
batate$ scala code/scala/constructor.scala
|
|
Outer constructor
|
|
Outer constructor
|
|
Inner constructor
|
That’s all there is to it. Auxiliary constructors are important because they allow for a broad array of usage patterns. Let’s look at how to create class methods.
So far, the classes have been pretty vanilla. We created a couple of basic classes with nothing more than attributes and methods. In this section, we’ll look at some of the ways that classes can interact.
In Java and Ruby, you create both class methods and instance methods within the same body. In Java, class methods have the static keyword. Ruby uses def self.class_method. Scala uses neither of these strategies. Instead, you will declare instance methods in the class definitions. When there’s something that can have only one instance, you’ll define it with the object keyword instead of the class keyword. Here’s an example:
| scala/ring.scala | |
object TrueRing {
|
|
def rule = println("To rule them all")
|
|
}
|
|
TrueRing.rule
|
|
The TrueRing definition works exactly like any class definition, but it creates a singleton object. In Scala, you can have both an object definition and a class definition with the same name. Using this scenario, you can create class methods within the singleton object declaration and instance methods within the class declaration. In our example, the method rule is a class method. This strategy is called companion objects.
Inheritance in Scala is pretty straightforward, but the syntax must be exact. Here’s an example of extending a Person class with Employee. Notice that the Employee has an additional employee number in the id field. Here’s the code:
| scala/employee.scala | |
class Person(val name: String) {
|
|
def talk(message: String) = println(name + " says " + message)
|
|
def id(): String = name
|
|
}
|
|
|
|
class Employee(override val name: String,
|
|
val number: Int) extends Person(name) {
|
|
override def talk(message: String) {
|
|
println(name + " with number " + number + " says " + message)
|
|
}
|
|
override def id():String = number.toString
|
|
}
|
|
|
|
val employee = new Employee("Yoda", 4)
|
|
employee.talk("Extend or extend not. There is no try.")
|
|
In this example, we’re extending the Person base class with Employee. We’re adding a new instance variable called number in Employee, and we’re also overriding the talk message to add some new behavior. Most of the tricky syntax is around the class constructor definition. Notice that you must specify the complete parameter list for Person, though you can omit the types.
The override keyword, both in the constructor and for any methods you want to extend from the base class, is mandatory. This keyword will keep you from inadvertently introducing new methods with misspellings. All in all, there are no major surprises here, but at times, I do feel a bit like Edward trying to pet a fragile baby bunny. Moving on....
Every object-oriented language must solve the problem that one object can have several different roles. An object can be a persistent, serializable shrubbery. You don’t want your shrubbery to have to know how to push binary data into MySQL. C++ uses multiple inheritance, Java uses interfaces, Ruby uses mixins, and Scala uses traits. A Scala trait is like a Ruby mixin, implemented with modules. Or, if you prefer, a trait is like a Java interface plus an implementation. Look at a trait as a partial-class implementation. Ideally, it should implement one critical concern. Here’s an example that adds the trait Nice to Person:
| scala/nice.scala | |
class Person(val name:String)
|
|
|
|
trait Nice {
|
|
def greet() = println("Howdily doodily.")
|
|
}
|
|
|
|
class Character(override val name:String) extends Person(name) with Nice
|
|
|
|
val flanders = new Character("Ned")
|
|
flanders.greet
|
|
The first element you see is Person. It is a simple class with a single attribute called name. The second element is the trait called Nice. That is the mixin. It has a single method called greet. The final element, a class called Character, mixes in the Nice trait. Clients can now use the greet method on any instance of Character. The output is what you would expect:
batate$ scala code/scala/nice.scala
|
|
Howdily doodily.
|
There’s nothing too complicated here. We can take our trait called Nice with a method called greet and mix it into any Scala class to introduce the greet behavior.
We covered a tremendous amount of territory in day 1 because we have to fully develop two different programming paradigms in one language. Day 1 showed that Scala embraces object-oriented concepts, running in the JVM side-by-side with existing Java libraries. Scala’s syntax is similar to that of Java and is also strongly and statically typed. But Martin Odersky wrote Scala to bridge two paradigms: object-oriented programming and functional programming. These functional programming concepts that we’ll introduce in day 2 will make it easier to design concurrent applications.
Scala’s static typing is also inferred. Users do not always need to declare types for all variables in all situations because Scala can often infer those types from syntactical clues. The compiler can also coerce types, such as integers to strings, allowing implicit type conversions when they make sense.
Scala’s expressions work much like they do in other languages, but they are a little more strict. Most conditionals must take a boolean type, and 0 or Nil will not work at all; they can substitute for neither true nor false. But there’s nothing dramatically different about Scala’s looping or control structures. Scala does support some more advanced types, like tuples (fixed-length lists with heterogeneous types) and ranges (a fixed, all-inclusive ordered sequence of numbers).
Scala classes work much like they do in Java, but they don’t support class methods. Instead, Scala uses a concept called companion objects to mix class and instance methods on the same class. Where Ruby uses mixins and Java uses interfaces, Scala uses a structure like a mixin called a Trait.
In day 2, we’ll take a full pass through Scala’s functional features. We’ll cover code blocks, collections, immutable variables, and some advanced built-in methods like foldLeft.
The first day of Scala covered a lot of ground, but it should be mostly familiar territory. These object-oriented concepts should be familiar to you. These exercises are a little advanced compared to the earlier exercises in the book, but you can handle it.
Find:
The Scala API
A comparison of Java and Scala
A discussion of val versus var
Do:
Write a game that will take a tic-tac-toe board with X, O, and blank characters and detect the winner or whether there is a tie or no winner yet. Use classes where appropriate.
Bonus problem: Let two players play tic-tac-toe.