December 2007: Why Scala?

Why Scala?

By Tim Dalton, OCI Software Engineer

December 2007


Introduction

Scala is a programming language that integrates features of object-oriented and functional programming languages. It is statically typed and compiles to JVM bytecode. A scripting interpreter is also part of the Scala language distribution. At the time of this writing, the current release of Scala for the JVM is version 2.6 with a significantly less mature .NET version beginning to get more attention. This document provides an overview of those aspects of Scala that make it an intriguing option for development on the JVM platform and perhaps eventually .NET as well.

Scala was invented at the EPFL (Ecole Polytechnique Federale de Lausanne) in Switzerland primarily by Martin Odersky, a professor there. Oderksy is the co-designer of Java generics and the original author of the javac reference compiler. The Scala language was first released in 2003.

Scala Basics

The Scala distribution requires 1.4 or later of the Java Runtime Environment (JRE). It can be downloaded from the Scala language download page. To run examples in this document, ensure that the ./bin directory of the Scala distribution is included on the execution path (PATH environment variable) and that the JAVA_HOME environment variable references the location of a JDK or JRE version 1.4 or later.

The best way to describe a language is to show code and explain what is happening.

The ubiquitous Hello World coded in Scala:

  1. package HelloWorldDemo
  2.  
  3. object Main {
  4. def main(args:Array[String]) =
  5. println("Hello World");
  6. }

Save the above text in a file named HelloWorldDemo.scala, compile via scalac HelloWorldDemo.scala, and execute using scala HelloWorldDemo.Main. That will result in the output, Hello World.

Actually, Scala source files do not need to have any particular name nor conform to any directory structure. The source for multiple Scala classes and other types documented here can be in a single file.

Features demonstrated in "HelloWorldDemo":

A more complex example:

  1. package SwingDemo
  2.  
  3. import javax.swing.{JFrame, JLabel}
  4.  
  5. object Main extends Application {
  6. def getTitle() = "Scala can Swing"
  7.  
  8. val frm = new JFrame(getTitle)
  9. frm.getContentPane.add(new JLabel("Hello World"))
  10. frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
  11. frm.pack
  12. frm setVisible true
  13. }

To one accustomed to Java, there appears to be typographical errors in the example above, but there are no errors and it will compile. Scala has a very flexible syntax as compared to Java and other statically typed languages. This allows for a greater degree of conciseness, code can become more like a "Domain Specific Language" (DSL) and new idioms can be invented. Many features of Scala are enabled by this syntactical flexibility.

Like Java, Scala uses curly braces {} to define the scope of an object, class, method, or other block of code. It does have a different notation for importing packages in that multiple classes from a given package can be specified using a curly brace notation and uses _ instead * to import all from a package.

Features demonstrated in "SwingDemo":

Scala Types

Scala is a pure object-oriented language and does not support primitives. In the scala package, there are classes CharByteShortIntLongFloatDouble, and Boolean that correlate to Java primitives. When Scala objects interact with Java, conversions between primitives and those types are implicitly performed.

Other Notable Syntax Features:

  1. var str = """This
  2. is
  3. a
  4. multi-line
  5. string"""
  6.  
  1. package FooPlusBarDemo
  2.  
  3. class Foo(value:Int) {
  4. def +(bar:Bar) = value + bar.value
  5. }
  6. class Bar(val value:Int)
  7.  
  8. object Main extends Application {
  9. val foo = new Foo(13)
  10. val bar = new Bar(23)
  11. println(foo + bar)
  12. }
  13.  

Running the above application should produce the output 36. The expression foo + bar could be also expressed as foo.+(bar). Scala has rules as to whether a method is usable as a infix, prefix, or postfix operation. All methods can be used as an infix operator like the foo + bar expression above or as postfix operators. However, methods that end with colon, :, are right associative instead of left associative. The following characters can be used for prefix operations, '+', '-', '!', and '~', that are defined using notation def unary_. Here is an example of unary/prefix operation, postfix, and a right associative infix operation:

  1. package PreInPostFixDemo
  2.  
  3. class Foo(val value:Int) {
  4. def unary_! = "!!!" + value + "!!!"
  5.  
  6. def % = value + "%"
  7.  
  8. def *:(multiple:Int) = value * multiple
  9. }
  10.  
  11. object Main extends Application {
  12. var foo = new Foo(62)
  13.  
  14. Console.println(!foo) // unary
  15. Console.println(foo %) // postfix
  16. Console.println(2 *: foo) // infix and right associative
  17. }

Output:

!!!62!!!
62% 
124

The expression !foo is the equivalent of foo.!foo % is the same as foo.%, and the expression 2 *: foo, being right associative, is the equivalent of foo.*:(2). The List data type, described later, utilizes right associative infix operators.

--Inferred return values based on result of last expression in code block
Scala does not require an explicit return in a method, though it is supported. Scala uses the result of the last line in the code block as the return value for the block. If the last line is a Java method returning void, then the method is inferred to return Unit. Example:

  1. def NullSafeToUpper(s:String) = {
  2. println("in NullSafeToUpper)
  3. if (s == null) "NULL" else s.toUpperCase
  4. }

This example demonstrates how the result of the last expression in a block of code is the inferred return value and that if constructs in Scala can also act like the <condition> ? <then-expression> : <else-expression> construct in Java. Since both possible results of if (s == null) are strings, then String is the inferred return type for the method.

-- Primary constructors
The primary constructor of a Scala object is defined as part of the class definition itself. Object properties can also be defined in the primary constructor. Example:

  1. package ValVarConstructorDemo
  2.  
  3. class ImmutableFoo(val value:Int)
  4.  
  5. class MutableFoo(var value:Int)
  6.  
  7. object Main extends Application {
  8. val immutable = new ImmutableFoo(54)
  9. val mutable = new MutableFoo(32)
  10. println("Immutable value = " + immutable.value)
  11. // Compiler won't allow: immutable.value = 65
  12. println("Mutable value = " + mutable.value)
  13. mutable.value = 39
  14. println("Mutated Mutable value = " + mutable.value)
  15. }

Output:

Immutable value = 54
Mutable value = 32
Mutated Mutable value = 39

Parameters provided to the primary constructor can use val or var to indicate whether properties of the same name should be defined for the object. Specifying a val makes the property accessible by an implicit accessor method, while var makes the property also mutable using an implicit mutator method. These accessors and mutators are implemented in a way that allows access in the same manner as a public field in Java. The value property in ImmutableFoo and MutableFoo classes in the above example demonstrates value and variable properties.

Scala can subclass based on a superclass constructor. Example:

  1. package DogSpeakDemo
  2.  
  3. class Mammal(name:String) {
  4. override def toString() = "Mammal named " + name
  5. }
  6.  
  7. class Dog(name:String) extends Mammal(name) {
  8. def speak() = println(this.toString + " says Woof")
  9. }
  10.  
  11. object Main extends Application {
  12. var rinTinTin = new Dog("Rin Tin Tin")
  13. rinTinTin.speak
  14. }

he example above will output Mammal named Rin Tin Tin says Woof. The Dog class extends the Mammal class and uses the Mammal(name:String) constructor to pass the name to the super class. (Note: Scala requires explicit override for overriding methods.)

Allowing constructors and fields to be specified as part of the class definition itself allows simple classes to be defined using a single line of code.

Secondary constructors can be defined by implementing this methods. The example below adds a secondary constructor that takes a double value for the AbsoluteNumber class:

  1. class AbsoluteNumber(num:Int) {
  2. var value = Math.abs(num)
  3.  
  4. def this(dbl:Double) = this(dbl.toInt)
  5. }

The this(dbl:Double) constructor simply invokes the primary constructor with the double value converted to an integer. Secondary constructor parameters do not become properties for the object.

-- Ability to specify accessors and mutators and create virtual public fields
Scala provides the ability to define accessor and mutator methods that hide the implementation of an object property. Example:

  1. package AbsoluteNumberDemo
  2.  
  3. class AbsoluteNumber(num:Int) {
  4. private var _value = Math.abs(num)
  5.  
  6. def value = _value // "getter" method
  7.  
  8. def value_=(num:Int) = _value = Math.abs(num) // "setter" method
  9. }
  10.  
  11. object Main extends Application {
  12. var absolute = new AbsoluteNumber(10)
  13. printf("Absolute = {0}\n", absolute.value)
  14. absolute.value = -5
  15. printf("Absolute = {0}\n", absolute.value)
  16. }

The example produces output:

Absolute = 10
Absolute = 5    

To a client object, an AbsoluteNumber object appears to have a public field named value. The def _=() notation specifies a mutator method that is used to perform assignment to the property in question. This allows Scala classes to better conform to the Uniform Access Principle 1 which states that the services on an object should be available through a uniform notation that does not reveal whether they are implemented through storage or through computation. The num parameter provided to the primary constructor becomes a hidden property.

  1. package ApplyDemo
  2.  
  3. object Foo {
  4. def apply(n:Int) = printf("FooObject({0})\n", n)
  5. }
  6.  
  7. class Foo {
  8. def apply(n:Int) = printf("FooClass({0})\n",n)
  9. }
  10.  
  11. object Main extends Application {
  12. Foo(1)
  13. var foo = new Foo
  14. foo(2)
  15. foo(3)
  16. }

Output:

FooObject(1)
FooClass(2)
FooClass(3) 

Expression "Foo(1) is the equivalent to Foo.apply(1). Scala arrays and other collections classes use the apply method to provide indexers. Array access in Scala is done with parenthesis and not square brackets like in Java.

-- Type Aliasing
Scala supports type aliasing which allows types to be defined as aliases to other types. Example:

  1. package TypeAliasDemo
  2.  
  3. object Main extends Application {
  4. type ArrayOfIntArrays = Array[Array[Int]]
  5. var arrs:ArrayOfIntArrays = Array(Array(1,2,3),Array(4,5,6),Array(7,8,9))
  6. printf("arrs(1)(2) = {0}\n", arrs(1)(2))
  7. }

Output:

arrs(1)(2) = 6

Type aliases can be inherited from super classes or traits. More about traits later.

Functional Programming with Scala

Functional programming is getting more serious consideration outside of academia because of characteristics that are advantageous for concurrent programming. A main reason for this is that a pure functional language maintains no global state. The stack represents the state and therefore function invocations that are not dependent on each other can easily run concurrently.

Though Scala cannot be considered a pure functional language, it borrows many features from popular functional languages. With a little discipline by developers, benefits can be reaped.

First Class Functions

Functions in Scala are objects and can be passed like any other object. Function objects are expressed as in form, [()] => . Example:

 
  1. package FunctionDemo
  2.  
  3. object Main extends Application {
  4. val multiply = (x:Int, y:Int) => x * y // value referencing function object
  5.  
  6. def add(x:Int, y:Int) = x + y // method of the Main object
  7.  
  8. val pow = (x:Int, y:Int) => { // more elaborate function object
  9. var i=0;
  10. var result = 1;
  11. while (i < y) {
  12. result = result * x;
  13. i = i + 1
  14. }
  15. result
  16. }
  17.  
  18. def doOper(x:Int, y:Int, func:(Int, Int) => Int) = func(x,y)
  19.  
  20. println("doOper(3,4, multiply) = " + doOper(3,4, multiply))
  21. println("doOper(3,4, add _) = " + doOper(3,4, add _))
  22. println("doOper(3,4, pow) = " + doOper(3,4, pow))
  23. println("doOper(3,4, (x:Int, y:Int) => x * 2 + y) = "
  24. + doOper(3,4, (x:Int, y:Int) => x * 2 + y))
  25. }

Output:

doOper(3,4, multiply) = 12
doOper(3,4, add _) = 7
doOper(3,4, pow) = 81
doOper(3,4, (x:Int, y:Int) => x * 2 + y) = 10

The doOper method takes two integers and a function object that takes two integers and returns an integer. The <function or method name> _ expression indicates a partially applied function. This prevents the compiler from interpreting the expression as an attempt to invoke the function. When used with a method, like the add _ expression above, a function object is generated that delegates to the add method. The doOper(3,4, (x:Int, y:Int) => x * 2 + y) expression passes an anonymous inline function object to the doOper method. Inline function objects like this are often referred to as Lambda Expressions.

A function object without parameters is a code block that can be passed around just like any other object. Example:

  1. package BlockDemo
  2.  
  3. object Main extends Application {
  4. def timeBlock(block: =>Unit) {
  5. val start = System.currentTimeMillis()
  6. block
  7. printf("Block took {0} milliseconds\n", System.currentTimeMillis - start)
  8. }
  9.  
  10. def repeat(n: Int)(block: =>Unit) = (1 to n).foreach { x =>
  11. block
  12. }
  13.  
  14. def test = {
  15. var str = "yada"
  16. repeat(2) { println(str) }
  17.  
  18. val fiveTimes = repeat(5)_
  19. str = "blah"
  20. fiveTimes {
  21. println(str)
  22. }
  23. }
  24.  
  25. timeBlock {
  26. (1 to 4).foreach { x => println("x = " + x) }
  27. }
  28. test
  29. }

Output:

x = 1
x = 2
x = 3
x = 4
Block took 32 milliseconds
yada
yada
blah
blah
blah
blah
blah   

The timeBlock method takes a block as a parameter and reports how many milliseconds it takes to execute it. The test method is used to demonstrate that values and variables in the scope of the method are accessible by the block. The ability to access values and variables in the current scope within code blocks represents a form of lexically scoped closures. (Note: the 1 to nexpression uses a to method on the Int class and is equivalent to 1.to(n) which returns a Range object that can be iterated over for each value between 1 and n.)

The multiple parameters lists for the repeat is one way that Scala supports currying. Currying is a technique in functional programming of reducing a function call containing multiple parameters to multiple function calls each with usually a single parameter. Each intermediate function call would return a function representing a partial application of the parameters. The expression repeat(5)_ represents a partially applied function which is a function object that takes a code block as parameter to complete the application of repeat. Nested functions provide another form of currying. Example:

  1. package NestedBlockDemo
  2.  
  3. object Main extends Application {
  4. def repeat(n: Int) = {
  5. def executeBlock(block: =>Unit) = (1 to n).foreach { x =>
  6. block
  7. }
  8. executeBlock _
  9. }
  10.  
  11. val sevenTimes = repeat(7)
  12.  
  13. sevenTimes {
  14. println("hello")
  15. }
  16. }

The executeBlock function is nested in the repeat method and it is partially applied as the return value.

Built-in support for List and Tuple Data Types

List and tuple types are very common in functional programming languages and Scala implements them both. Both types are immutable. Example:

  1. package TuplesAndListsDemo
  2.  
  3. object Main extends Application {
  4. val list1 = 1 :: 2 :: 3 :: 5 :: 8 :: Nil // List[Int] inferred
  5. val list2 = List(13, 21, 34) // Another List[Int]
  6.  
  7. println("list1.head = " + list1.head)
  8. println("list1.tail = " + list1.tail)
  9. val concatenated = list1 ::: list2
  10. println("list1 and list2 concatenated = " + concatenated)
  11. println("concatenated list maps to square = " + concatenated.map(x => x * x))
  12. println("concatenated list even numbers = " + concatenated.filter(_ % 2 ==0))
  13.  
  14. val tuple1 = (1, "one", 1.0)
  15. val tuple2 = (1, "two", 2.0)
  16. println("tuple1._1 = " + tuple1._1)
  17. println("tuple2._3 = " + tuple2._3)
  18.  
  19. }

Output:

list1.head = 1
list1.tail = List(2, 3, 5, 8)
list1 and list2 concatenated = List(1, 2, 3, 5, 8, 13, 21, 34)
concatenated list maps to square = List(1, 4, 9, 25, 64, 169, 441, 1156)
concatenated list even numbers = List(2, 8, 34)
tuple1._1 = 1
tuple2._3 = 2.0

The list1 and list2 values demonstrate two ways to define a list in Scala. The list1 value uses a special instance of List called Nil that represents an empty list and then uses the right-associative operator, ::, repeatedly. An equivalent expression is val list1 = Nil.::(8).::(5).::(3).::(2).::(1).

The implicitly imported scala package defines a List singleton object with an apply method that accepts a variable numbers of arguments of a parameterized type. The parameterized type usually can be inferred. Scala supports variable arguments like Java does in 1.5 and later. This apply method acts as a factory and returns an implementation of the List class. The assignment expression for list2 uses this method. Both forms of list instantiation demonstrate Scala syntax enabling other features.

Lists in Scala support methods to return the first item, head, or the rest of the list, tail. Other common list operations like map and filter are provided as well. Map methods apply a function object to each item in the list and return a new list with the result from each. Filter methods apply a function object that returns a boolean to each item and return a new list of items that evaluated to true. The _ % 2 expression is shorthand for a function object that could be expressed as x:T => x % 2 where T is the type of the item in the list or as x => x % 2 since the type can be inferred.

Tuples are groupings of objects of differing types. Scala Tuple objects have methods in the form_ where  is number from 1 to the total number of objects in the tuple.

Pattern matching

Another common feature of functional languages is pattern matching. In Scala, pattern matching is not quite as integrated in to the language as it is in Erlang or OCaml where functions themselves have multiple definitions and the one invoked is based on how arguments match patterns. Scala provides a match/case construct. Example:

  1. package PatternDemo
  2.  
  3. case class Person(name:String, age:Int)
  4.  
  5. object Main extends Application {
  6.  
  7. def doMatch(any:Any) = any match {
  8. case x:Int => "Integer = " + x
  9. case x:Float => "Float = " + x
  10. case x::10::rest => "List head = " + x + " second = ten rest = " + rest
  11. case x::rest => "List head = " + x + " rest = " + rest
  12. case (x,10) => "Tuple, _1 = " + x + ", ten"
  13. case (x:Int,y:Int) if (x + y) == 10 => "Tuple = (" + x + "," + y + ") sum = 10"
  14. case (x,y) => "Tuple = (" + x + "," + y + ")"
  15. case Person(name, age) if (age < 18) => "Young Person named " + name
  16. case Person(name, age) => "Adult Person named " + name
  17. case s:String => "String = '" + s + "'"
  18. case _ => "Something else"
  19. }
  20.  
  21. println(doMatch(1))
  22. println(doMatch(1.0F))
  23. println(doMatch(7::10::19::Nil))
  24. println(doMatch(1::2::Nil))
  25. println(doMatch(7.5::2.5::Nil))
  26. println(doMatch((1,2)))
  27. println(doMatch((3,10)))
  28. println(doMatch((4,6)))
  29. println(doMatch(new Person("Tim", 40)))
  30. println(doMatch(Person("John", 6)))
  31. println(doMatch("string"))
  32. println(doMatch(new java.util.Date()))
  33. }

Output

Integer = 1
Float = 1.0
List head = 7 second = ten rest = List(19)
List head = 1 rest = List(2)
List head = 7.5 rest = List(2.5)
Tuple = (1,2)
Tuple, _1 = 3, ten
Tuple = (4,6) sum = 10
Adult Person named Tim
Young Person named John
String = 'string' 
Something else

The first case that matches the passed value is processed and only that one. There is no need for something like break as in Java.

Case clauses can extract values from the matching pattern and use them as values in the corresponding execute block. For example, the case x::10::rest clause matches any List with a second value equal to 10. The value x is assigned the first item of the list and the rest value will reference a List containing the remainder of items or Nil if there are none.

case class in Scala is a class that adds functionality that enables pattern matching for that class. Added functionality includes generation of a companion class with a factory apply method that creates an instance of the class. Hence, case classes can be instantiated without new. The Person("John", 6) expression uses the apply method on the Person singleton object as a factory to create an instance of the Person class and is equivalent to new Person("John", 6). Case class companions objects also have methods to allow fields to be extracted and used in the execute block for the case clause. Two case Person clauses in the above example extract the name field.

Case clauses can use if conditionals that are called "guards" to further specify the match.

The case _ clause is a "match anything" clause that acts like a default: clause on a Java switch statement.

One area of Java that represents a simple form of pattern matching is exception handling. Scala uses its own pattern matching constructs to process exceptions. Example:

  1. try {
  2. // Exception prone code
  3. } catch {
  4. case ex: java.io.FileNotFoundException => System.err.println("File Not Found")
  5. case ex: java.io.EOFException => System.err.println("Unexpected EOF")
  6. case ex: Exception => System.err.println("Other Exception " + ex.getMessage)
  7. }

Other Scala Language Features

Scala has many advanced features as compared to Java. Prominent features are outlined below:

Traits

Traits are an improvement over Java interfaces in that they can contain implementation code. They are similar to Ruby mixins and provide a form of multiple inheritance. Scala classes can use many traits. Example:

  1. package FooBarTraitDemo
  2.  
  3. trait Foos {
  4. def doFoo(text:String) = printf("{0}: Foo(\"{1}\")\n", this.toString, text)
  5. }
  6.  
  7. trait Bars {
  8. def doBar(text:String) = printf("{0}: Bar(\"{1}\")\n", this.toString, text)
  9. }
  10.  
  11. class FooClass extends Foos {
  12. override def toString = "FooClass"
  13. }
  14.  
  15. class BarClass extends Bars {
  16. override def toString = "FooClass"
  17. }
  18.  
  19. class FooBarClass extends Bars with Foos {
  20. override def toString = "FooBarClass"
  21. }
  22.  
  23. class Now {
  24. override def toString = new java.util.Date().toString()
  25. }
  26.  
  27. object Main extends Application {
  28. val foo = new FooClass
  29. val bar = new BarClass
  30. val fooBar = new FooBarClass
  31.  
  32. foo.doFoo("One")
  33. bar.doBar("Two")
  34.  
  35. fooBar.doFoo("Three")
  36. fooBar.doBar("Four")
  37.  
  38. val fooBarNow = new Now with Foos with Bars
  39.  
  40. fooBarNow.doFoo("Five")
  41. fooBarNow.doBar("Six")
  42. }

Output:

FooClass: Foo("One")
FooClass: Bar("Two")
FooBarClass: Foo("Three")
FooBarClass: Bar("Four")
Sat Dec 01 12:58:33 CST 2007: Foo("Five")
Sat Dec 01 12:58:33 CST 2007: Bar("Six")

The FooClass and BarClass have the Foos and Bars traits respectively while FooBarClass has both traits. Traits can override methods on the objects that use them. The expression, new Now with Foos with Bars, demonstrates that an object with traits can be declared anonymously like an inner class in Java. The fooBarNow value is assigned an object that is a subclass of Now that has both the Foos and Bars traits.

Sequence comprehensions

Sequence comprehensions (also called "for comprehensions") provide a syntax to iterate over multiple enumerations, apply conditionals, and either produce a new sequence or execute a function object on each item. Example:

  1. package ForDemo
  2.  
  3. case class Team(name:String, score:Int)
  4.  
  5. object Main extends Application {
  6. var evens = for (i<- 1 to 100 if i % 2 == 0) yield i;
  7. println("Evens = " + evens)
  8.  
  9. var teams = List(Team("Klingons", 132)
  10. ,Team("Daleks", 93)
  11. ,Team("Kzinti", 66)
  12. ,Team("Martians", 65)
  13. ,Team("Vogons", 55)
  14. ,Team("Ewoks", 33))
  15.  
  16. for (team1<-teams;
  17. team2<-teams if team1 != team2 && team1.score > team2.score) {
  18. println(team1.name + " defeated " + team2.name)
  19. }
  20. }

Output:

Evens = RangeFM(2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34,
36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74,
76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100)
Klingons defeated Daleks
Klingons defeated Kzinti
Klingons defeated Martians
Klingons defeated Vogons
Klingons defeated Ewoks
Daleks defeated Kzinti
Daleks defeated Martians
Daleks defeated Vogons
Daleks defeated Ewoks
Kzinti defeated Martians
Kzinti defeated Vogons
Kzinti defeated Ewoks
Martians defeated Vogons
Martians defeated Ewoks
Vogons defeated Ewoks

The first sequence comprehension in the above example iterates over a range of integers from 1 to 100 and returns a new sequence containing even numbers. The second one has two iterations over the same sequence nested to compare the scores of each team to all other teams and produce output indicating the result. Sequence comprehensions provide a sort of query language for sequences.

Implicit Conversion Methods

Scala supports implicit methods that are often used for converting types. If the compiler encounters a type mismatch, it will look for an implicit method that takes the specified type and returns the required type. Example:

  1. package ImplicitsDemo
  2.  
  3. class Foo(val value:Int) {
  4. override def toString = "Foo(" + value + ")"
  5. }
  6.  
  7. class Bar(val value:String) {
  8. def printValue = Console.println(value)
  9. }
  10.  
  11. object Main extends Application {
  12. implicit def Foo2Bar(foo:Foo) = new Bar(foo.value.toString)
  13.  
  14. def printBar(bar:Bar) = bar.printValue
  15.  
  16. printBar(new Foo(42))
  17. }

The printBar method expects a Bar object, but is provided a Foo object. The compiler will implicitly insert a call to Foo2Bar since it takes a Foo and returns a Bar. The method name is not significant here. An error will result if the compiler finds multiple matching implicit methods. Explicitly, the last line would be printBar(new Bar(new Foo.value.toString)). The scala.Predef object provides many implicit conversion methods with self-explanatory names like byte2int and int2long to perform various conversions. Similar concepts in Java would be the implicit call to Object.toString() when an object is passed to various print methods and auto-boxing of primitives to objects when needed.

XML Processing

Scala supports XML as a built-in data type and includes operations that are similar to XPath expressions for querying the document object model (DOM). Example:

  1. package XMLDemo
  2.  
  3. object Main extends Application {
  4. val xmlDoc = <phonebook>
  5. <entry>
  6. <name>Joseph Blow</name>
  7. <number type="home">312-542-2311</number>
  8. <number type="mobile">526-321-8729</number>
  9. </entry>
  10. <entry>
  11. <name>John Q. Public</name>
  12. <number type="home">526-442-9332</number>
  13. <number type="mobile">312-333-1228</number>
  14. </entry>
  15. <entry>
  16. <name>Jane Doe</name>
  17. <number type="home">526-412-5223</number>
  18. <number type="mobile">312-231-2193</number>
  19. </entry>
  20. </phonebook>
  21.  
  22. println("Names:")
  23. (xmlDoc\\"name").map(_.text).foreach(println)
  24. println("\nNumbers in 526 Area Code:")
  25. (xmlDoc\\"number").filter(_.text.startsWith("526")).map(_.text).foreach(println)
  26. }

Output:

Names:
Joseph Blow
John Q. Public
Jane Doe
 
Numbers in 526 Area Code:
526-321-8729
526-442-9332
526-412-5223

In the above example, the map and filter functions transform the results of the \\ operations. The _.text expression is shorthand for a function object, x:T => x.text where T is the parameterized type of the list being mapped or filtered. Sequence comprehensions could be used on XML as well for more concise expressions. The following section of code is more readable and could be used in the XMLDemo example above:

  1. println("Names:")
  2. for (name <- xmlDoc\\"name") println(name.text);
  3. println("\nNumbers in 526 Area Code:")
  4. for (number <- xmlDoc\\"number" if number.text.startsWith("526")) println(number.text);

Scala code can be embedded in the XML within curly braces {}. Below is an example of a servlet implemented in Scala that uses XML with embedded values:

  1. package ServletDemo
  2.  
  3. import javax.servlet.http.HttpServlet
  4. import javax.servlet.http.HttpServletRequest
  5. import javax.servlet.http.HttpServletResponse
  6.  
  7. class HelloServlet extends HttpServlet {
  8. override def doGet(request: HttpServletRequest
  9. ,response: HttpServletResponse):unit =
  10. {
  11. var user = request.getParameter("user")
  12. if (user == null) {
  13. user = ""
  14. }
  15. var html =
  16. <html>
  17. <head>
  18. </head>
  19. <body>
  20. <h1>Hello { user }</h1>
  21. <form>
  22. User: <input type="text" name="user" length="16"/>
  23. </form>
  24. </body>
  25. </html>
  26. response.getWriter.println(html)
  27. }
  28. }

The user value is retrieved from the servlet parameters and is embedded in the HTML to be rendered.

Actors Library

This feature was inspired by a similar concept in the functional language, Erlang. Actors are basically concurrent processes that communicate via message passing. Actors support both synchronous and asynchronous message passing. Example:

  1. package ActorDemo
  2.  
  3. import scala.actors.Actor
  4. import scala.actors.Actor._
  5.  
  6. case class Stop;
  7.  
  8. class Initiator(receiver:Actor) extends Actor {
  9. def act() {
  10. receiver ! "Can you here me now?"
  11. receive {
  12. case response:String => {
  13. println("Initiator received response:\n" + response)
  14. }
  15. }
  16. receiver ! Stop
  17. }
  18. }
  19.  
  20. class Receiver extends Actor {
  21. def act() {
  22. while (true) {
  23. receive {
  24. case msg:String => {
  25. println("Receiver received:\n" + msg)
  26. sender ! "Yes I can."
  27. }
  28. case _:Stop => {
  29. println("Receiver received Stop.")
  30. exit()
  31. }
  32. }
  33. }
  34. }
  35. }
  36.  
  37. object Main extends Application {
  38. val receiver = new Receiver;
  39. val initiator = new Initiator(receiver)
  40. receiver.start
  41. initiator.start
  42. }

Output:

Receiver received:
Can you here me now?
Initiatator received response:
Yes I can.
Receiver received Stop.

Actors implement the method act which is analogous to the Runnable.run() method in Java. A ! method is used to send a message to the receiving actor. Message queues are used to store messages until the receiver is ready to process them.

Scala actors use a thread pool that initially contains four threads. When an actor blocks via receive a thread is blocked and is not available for the pool. The actor library will grow the pool if a new actor needs a thread.

The Scala actors library provides a means for an actor to not consume a thread when blocked by using a combination of loop and react methods. Due to the way react is implemented, looping using while does not work. Example of the Receiver class using loop and react:

  1. class Receiver extends Actor {
  2. def act() {
  3. loop {
  4. react {
  5. case msg:String => {
  6. println("Receiver received:\n" + msg)
  7. sender ! "Yes I can."
  8. }
  9. case _:Stop => {
  10. println("Receiver received Stop.")
  11. exit()
  12. }
  13. }
  14. }
  15. }
  16. }

This form of actor is called an "Event-based actor" and is much more scalable than the thread-based counterpart.

Lazy Evaluation

This feature was new as of Scala version 2.6. It allows an object property not to be evaluated until the value is accessed. Below is a code sample illustrating this:

  1. package LazyEvalDemo
  2.  
  3. import java.lang.Math // scala.Math object lacks "PI"
  4. import java.awt.Point
  5.  
  6. class Polar(val angle:Int,val distance:Int) {
  7. def cartesianX = distance * Math.cos(angle * (Math.PI/180))
  8.  
  9. def cartesianY = distance * Math.sin(angle * (Math.PI/180))
  10.  
  11. val eagerCartesianPoint = {
  12. println("eagerCartesianPoint being instantiated");
  13. new Point(cartesianX.toInt, cartesianY.toInt)
  14. }
  15.  
  16. lazy val lazyCartesianPoint = {
  17. println("lazyCartesianPoint being instantiated");
  18. new Point(cartesianX.toInt, cartesianY.toInt)
  19. }
  20. }
  21.  
  22. object Main extends Application {
  23. val polar = new Polar(30,10)
  24. println("Angle:\t" + polar.angle)
  25. println("Distance:\t" + polar.distance)
  26. println("Cartesian X:\t" +polar.cartesianX)
  27. println("Cartesian Y:\t" +polar.cartesianY)
  28. println("Eager Cartesian Point:\t" + polar.eagerCartesianPoint.toString)
  29. println("Lazy Cartesian Point:\t" + polar.lazyCartesianPoint.toString)
  30. }

Output:

eagerCartesianPoint being instantiated
Angle:  35
Distance:       10
Cartesian X:    8.191520442889917
Cartesian Y:    5.7357643635104605 
Eager Cartesian Point:  java.awt.Point[x=8,y=5]
lazyCartesianPoint being instantiated
Lazy Cartesian Point:   java.awt.Point[x=8,y=5]

The property eagerCartesianPoint is instantiated upon construction of the Polar object while the lazyCartesianPoint property is not instantiated until it is accessed. Lazy evaluation is useful in preventing waste as the result of unused resources without implicit coding.

Anonymous Typing

Sometimes also referred to as "Structural Typing", this is another feature introduced in version 2.6. It allows a form of "duck typing" when a type can be declared based on the methods implemented. Any classes that implement those methods match that type.

  1. package DuckTypeDemo
  2.  
  3. class Foo {
  4. def quack:String = "Foo - quacks"
  5. def waddle(distance:Int, units:String) = "Foo waddles " + distance + " " + units
  6. }
  7.  
  8. class Bar {
  9. def quack:String = "Bar - quacks"
  10. def waddle(distance:Int, units:String) = "Bar waddles " + distance + " " + units
  11. }
  12.  
  13. object Main extends Application {
  14. type Duck = {
  15. def quack:String;
  16. def waddle(d:Int, u:String):String
  17. }
  18.  
  19. val duckList = List[Duck](new Foo, new Bar)
  20.  
  21. duckList.map(_.quack).foreach(println)
  22. duckList.map(_.waddle(10,"feet")).foreach(println)
  23. }

Output:

Foo - quacks
Bar - quacks
Foo waddles 10 feet
Bar waddles 10 feet    

In the preceding example, classes Foo and Bar do not share an interface or a trait, but do implement the methods specified in the Duck type alias and therefore can comprise a list of type Duck. In other words, they quack like a Duck and waddle like a Duck and therefore both can be considered a Duck.

Summary

Scala's features make it as close to a dynamic language as a statically type language can get and yet maintain the performance characteristics of compiled Java. Scala approximates features that make dynamic languages like Ruby and Groovy attractive. Functional language features allow Scala programs to follow a more functional style when it is better suited for the task at hand or take a non-functional or imperative approach.

Features of Scala will certainly get consideration for inclusion in future versions of Java, particularly type inference and first class functions in the form of closures. There is a precedent for this in that Martin Odersky provided the basis for Java generics.

The unique characteristics of Scala as compared to other languages for the JVM platform make it compelling language for Java developers to learn and perhaps come to use.

References

Footnotes

[1] Object-Oriented Software Construction by Bertrand Meyer

 

The Software Engineering Tech Trends is a monthly publication featuring emerging trends in software engineering.

Subscribe

© Copyright Object Computing, Inc. 1993, 2018. All rights reserved

secret