Functional tendencies
While supporting all of the object-oriented features available in Java (and in fact, augmenting them in various ways), Scala also provides a large number of capabilities that are normally found only in functional programming languages. Together, these features allow Scala programs to be written in an almost completely functional style and also allow functional and object-oriented styles to be mixed.
Examples are:
No distinction between statements and expressions
Type inference
Anonymous functions with capturing semantics (i.e., closures)
Immutable variables and objects
Lazy evaluation
Delimited continuations (since 2.8)
Higher-order functions
Nested functions
Currying
Pattern matching
Algebraic data types (through case classes)
Tuples
Unlike C or Java, but similar to languages such as Lisp, Scala makes no distinction between statements and expressions. All statements are in fact expressions that evaluate to some value. Functions that would be declared as returning void in C or Java, and statements like while that logically do not return a value, are in Scala considered to return the type Unit, which is a singleton type, with only one object of that type. Functions and operators that never return at all (e.g. the throw operator or a function that always exits non-locally using an exception) logically have return type Nothing, a special type containing no objects; that is, a bottom type, i.e. a subclass of every possible type. (This in turn makes type Nothing compatible with every type, allowing type inference to function correctly.)
Similarly, an if-then-else "statement" is actually an expression, which produces a value, i.e. the result of evaluating one of the two branches. This means that such a block of code can be inserted wherever an expression is desired, obviating the need for a ternary operator in Scala:
For similar reasons, return statements are unnecessary in Scala, and in fact are discouraged. As in Lisp, the last expression in a block of code is the value of that block of code, and if the block of code is the body of a function, it will be returned by the function.
To make it clear that all functions are expressions, even methods that return Unit are written with an equals sign
or equivalently (with type inference, and omitting the unnecessary newline):
Due to type inference, the type of variables, function return values, and many other expressions can typically be omitted, as the compiler can deduce it. Examples are val x = "foo" (for an immutable constant or immutable object) or var x = 1.5 (for a variable whose value can later be changed). Type inference in Scala is essentially local, in contrast to the more global Hindley-Milner algorithm used in Haskell, ML and other more purely functional languages. This is done to facilitate object-oriented programming. The result is that certain types still need to be declared (most notably, function parameters, and the return types of recursive functions), e.g.
or (with a return type declared for a recursive function)
In Scala, functions are objects, and a convenient syntax exists for specifying anonymous functions. An example is the expression x => x < 2, which specifies a function with one parameter, that compares its argument to see if it is less than 2. It is equivalent to the Lisp form (lambda (x) (< x 2)). Neither the type of x nor the return type need be explicitly specified, and can generally be inferred by type inference; but they can be explicitly specified, e.g., as (x: Int) => x < 2 or even (x: Int) => (x < 2): Boolean.
Anonymous functions behave as true closures in that they automatically capture any variables that are lexically available in the environment of the enclosing function. Those variables will be available even after the enclosing function returns, and unlike in the case of Java's anonymous inner classes do not need to be declared as final. (It is even possible to modify such variables if they are mutable, and the modified value will be available the next time the anonymous function is called.)
An even shorter form of anonymous function uses placeholder variables: For example, the following:
list map { x => sqrt(x) }
can be written more concisely as
list map { sqrt(_) }
or even
list map sqrt
Scala enforces a distinction between immutable and mutable variables. Mutable variables are declared using the var keyword and immutable values are declared using the val keyword.
A variable declared using the val keyword cannot be reassigned in the same way that a variable declared using the final keyword can't be reassigned in Java. vals are only shallowly immutable, that is, an object referenced by a val is not guaranteed to itself be immutable.
Immutable classes are encouraged by convention however, and the Scala standard library provides a rich set of immutable collection classes.
Scala provides mutable and immutable variants of most collection classes, and the immutable version is always used unless the mutable version is explicitly imported.
The immutable variants are persistent data structures that always return an updated copy of an old object instead of updating the old object destructively in place.
An example of this is immutable linked lists where prepending an element to a list is done by returning a new list node consisting of the element and a reference to the list tail.
Appending an element to a list can only be done by prepending all elements in the old list to a new list with only the new element.
In the same way, inserting an element in the middle of a list will copy the first half of the list, but keep a reference to the second half of the list. This is called structural sharing.
This allows for very easy concurrency — no locks are needed as no shared objects are ever modified.
Evaluation is strict ("eager") by default. In other words, Scala evaluates expressions as soon as they are available, rather than as needed. However, it is possible to declare a variable non-strict ("lazy") with the lazy keyword, meaning that the code to produce the variable's value will not be evaluated until the first time the variable is referenced. Non-strict collections of various types also exist (such as the type Stream, a non-strict linked list), and any collection can be made non-strict with the view method. Non-strict collections provide a good semantic fit to things like server-produced data, where the evaluation of the code to generate later elements of a list (that in turn triggers a request to a server, possibly located somewhere else on the web) only happens when the elements are actually needed.
Functional programming languages commonly provide tail call optimization to allow for extensive use of recursion without stack overflow problems. Limitations in JVM bytecode complicate tail call optimization on the JVM. In general, a function that calls itself with a tail call can be optimized, but mutually recursive functions cannot. Trampolines have been suggested as a workaround. Trampoline support has been provided by the Scala library with the object scala.util.control.TailCalls since Scala 2.8.0 (released 14 July 2010). A function may optionally be annotated with @tailrec, in which case it will not compile unless it is tail recursive.
An example of this optimization could be implemented using the factorial definition. For instance, the recursive version of the factorial: Could be optimized to the tail recursive version like this: However, this could compromise composability with other functions because of the new argument on its definition, so it is common to use closures to preserve its original signature: This ensures tail call optimization and thus prevents a stack overflow error.
Scala has built-in support for pattern matching, which can be thought of as a more sophisticated, extensible version of a switch statement, where arbitrary data types can be matched (rather than just simple types like integers, Booleans and strings), including arbitrary nesting. A special type of class known as a case class is provided, which includes automatic support for pattern matching and can be used to model the algebraic data types used in many functional programming languages. (From the perspective of Scala, a case class is simply a normal class for which the compiler automatically adds certain behaviors that could also be provided manually, e.g., definitions of methods providing for deep comparisons and hashing, and destructuring a case class on its constructor parameters during pattern matching.)
An example of a definition of the quicksort algorithm using pattern matching is this:
The idea here is that we partition a list into the elements less than a pivot and the elements not less, recursively sort each part, and paste the results together with the pivot in between. This uses the same divide-and-conquer strategy of mergesort and other fast sorting algorithms.
The match operator is used to do pattern matching on the object stored in list. Each case expression is tried in turn to see if it will match, and the first match determines the result. In this case, Nil only matches the literal object Nil, but pivot :: tail matches a non-empty list, and simultaneously destructures the list according to the pattern given. In this case, the associated code will have access to a local variable named pivot holding the head of the list, and another variable tail holding the tail of the list. These variables are read-only, and are semantically very similar to variable bindings established using the let operator in Lisp and Scheme.
Pattern matching also happens in local variable declarations. In this case, the return value of the call to tail.partition is a tuple — in this case, two lists. (Tuples differ from other types of containers, e.g. lists, in that they are always of fixed size and the elements can be of differing types — although here they are both the same.) Pattern matching is the easiest way of fetching the two parts of the tuple.
The form _ < pivot is a declaration of an anonymous function with a placeholder variable; see the section above on anonymous functions.
The list operators :: (which adds an element onto the beginning of a list, similar to cons in Lisp and Scheme) and ::: (which appends two lists together, similar to append in Lisp and Scheme) both appear. Despite appearances, there is nothing "built-in" about either of these operators. As specified above, any string of symbols can serve as function name, and a method applied to an object can be written "infix"-style without the period or parentheses. The line above as written:
qsort(smaller) ::: pivot :: qsort(rest)
could also be written thus:
qsort(rest).::(pivot).:::(qsort(smaller))
in more standard method-call notation. (Methods that end with a colon are right-associative and bind to the object to the right.)
In the pattern-matching example above, the body of the match operator is a partial function, which consists of a series of case expressions, with the first matching expression prevailing, similar to the body of a switch statement. Partial functions are also used in the exception-handling portion of a try statement:
Finally, a partial function can be used alone, and the result of calling it is equivalent to doing a match over it. For example, the prior code for quicksort can be written thus:
Here a read-only variable is declared whose type is a function from lists of integers to lists of integers, and bind it to a partial function. (The single parameter of the partial function is never explicitly declared or named.) However, we can still call this variable exactly as if it were a normal function: