Scala is a multiparadigm language that compiles to Java bytecodes. We will be examining the way that Scala combines the object-oriented features of Java with functional constructs.
These notes were heavily influenced by the lecture slides developed by Cay Horstmann who taught the functional parts of his fall 2008 Progamming Languages course using Scala. If you like what you see, you can explore Cay's course materials yourself.
The main web page for Scala is at http://www.scala-lang.org/. You can download Scala from that site.
It's hard to predict what programming languages will end up being influential, but there is very good buzz around Scala. Read, for example, an interview with Twitter developers talking about their choice of Scala over Ruby for certain parts of their system.
I pointed out that Scala has many things in common with ML and Scheme:
It has a Read-Eval-Print-Loop interpreter that allows you to type expressions and see what they evaluate to
It has basic types for storing numbers (Int, Double), strings (String), boolean values (Boolean), and characters (Char)
It uses the keyword "val" to introduce an immutable binding for an identifier (same as in ML) and uses the keyword "var" to introduce a mutable variable binding (we didn't explore this much because we were looking at the more functional aspects of the language).
It has anonymous functions with a syntax similar to that used in ML. For example:
Everything is an object. It has a type called "Any" that is like Java's Object type. It is the superclass of all classes.
It has an if/then/else construct similar to ML but with required parentheses (as in Java) and Java style equality comparisons (==, !=) and without the keyword "then" and with a type that matches the lowest common ancestor in the type hierarchy:
if (2 < 3) 1 else 4
if (2 + 2 == 4) 1 else "hello"
if (2 + 2 != 4) 1 else "hello"
You define functions in a very similar manner to ML but using the keyword "def" instead of the keyword "fun":
def f(n : Int) = 2 * n
def fact(n : Int) : Int = if (n == 0) 1 else n * fact(n - 1)
def fact(n : BigInt) : BigInt = if (n == 0) 1 else n * fact(n - 1)
Scala has a limited ability to infer types. For example, you normally don't have to tell Scala the types of val or var bindings. It can infer the return types of nonrecursive functions, but not of recursive functions (that's because it encounters a call on the recursive function before it's had a chance to determine its type).
There is a List type that is similar to ML's list type. The empty list is Nil and you can use the :: operator to construct a list. The list type will be the lowest common ancestor of the types of values inserted into the list.
Nil
2 :: Nil
2.8 :: 2 :: Nil
"hello" :: 2.8 :: 2 :: Nil
You can also enumerate the values:
List("hello", 2.8, 2)
List objects have methods called head and tail, although you call them using the object dot notation without parentheses (list.head, list.tail). As a result, you can write recursive functions that are similar to ML functions:
def stutter(lst : List[Any]) : List[Any] =
if (lst.isEmpty) Nil else
lst.head :: lst.head :: stutter(lst.tail)
Note: can't put else on the next line because there is a simple form of if (like Java's if without an else) and line breaks are meaningful.
The List class has many higher order functions built in:
As in Ruby and Python, there is a Range type and a foreach construct:
1 to 10
for (n <- 1 to 10) println(n)
("blastoff" /: (1 to 10)) {(x, y) => y + ", " + x}
Scala has tuples that are similar to what we saw in ML:
(2, 3)
(3, "hello", 14.5)
val (x, y, z) = (3, "hello", 14.5)
Class definitions can be much simpler than in Java. For example, this declaration:
class Data(val n : Int, var x : Double)
Introduces the class, a constructor that takes two arguments, accessor methods for n and x (called n and x), and a mutator for x called x_$eq.
Scala, like Java, has an ability to use reflection to ask an object about itself (its class, its methods, etc). Unlike Java, the syntax to access that information is a little more convenient, as in:
class Data(val n : Int, var x : Double)
val a = new Data(3, 14.5)
a.getClass
for (m <- a.getClass.getMethods) println(m)
And much more that we didn't have time to look at including: closures, option type, overloaded operators, continuations, curried functions and partially instantiated functions, Map and Set collections, support for concurrency, and some limited mixin and pattern matching capabilities.
The complete log of the Scala session from the lecture is available here.