Scala Overview
Scala Overview
Martin Odersky, Philippe Altherr, Vincent Cremet, Burak Emir, Sebastian Maneth,
Stéphane Micheloud, Nikolay Mihaylov, Michel Schinz, Erik Stenman, Matthias Zenger
Taken together, these constructs make it easy to ex- Even though their syntax is dierent, Scala programs can
press autonomous components using Scala libraries inter-operate without problems with Java programs. In
without need for special language constructs (Sec- the example above, the Scala program invokes methods
tion 9). startsWith and substring of String, which is a class de-
ned in Java. It also accesses the static out eld of the Java
Scala allows external extensions of components using class System, and invokes its (overloaded) println method.
views (Section 10). This is possible even though Scala does not have a concept
of static class members. In fact, every Java class is seen in
Scala is currently implemented on the Java (Section 11)
Scala as two entities, a class containing all dynamic mem-
and .NET (Section 12) platforms.
bers and a singleton object, containing all static members.
Section 13 discusses related work and Section 14 concludes. Hence, System.out is accessible in Scala as a member of the
object System.
2 A Java-Like Language Even though it is not shown in the example, Scala classes
and objects can also inherit from Java classes and implement
Java interfaces. This makes it possible to use Scala code in
Scala is designed to interact well with mainstream platforms
such as Java or C#. It shares with these languages most of
a Java framework. For instance, a Scala class might be de-
2
Sometimes it is necessary to convert between the two repre- Scala treats operator names as ordinary identiers. More
sentations, for example when an instance of a value class is precisely, an identier is either a sequence of letters and
seen as an instance of the root class Any. These boxing con- digits starting with a letter, or a sequence of operator char-
versions and their inverses are done automatically, without acters. Hence, it is possible to dene methods called +, <=,
explicit programmer code. or ::, for example. Next, Scala treats every occurrence of
Note that the value class space is at; all value classes an identier between two expressions as a method call. For
are subtypes from scala.AnyVal, but they do not subtype instance, in Listing 1, one could have used the operator syn-
each other. Instead there are views (i.e. standard coercions, tax (arg startsWith "-") as syntactic sugar for the more
see Section 10) between elements of dierent value classes. conventional syntax (arg.startsWith("-")).
We considered a design alternative with subtyping between As an example how user-dened operators are declared
value classes. For instance, we could have made Int a sub- and applied, consider the following implementation of a
type of Float, instead of having a standard coercion from class Nat for natural numbers. This class (very ine-
Int to Float. We refrained from following this alternative, Zero
ciently) represents numbers as instances of two classes
because we want to maintain the invariant that interpreting and Succ. The number N would hence be represented as
a value of a subclass as an instance of its superclass does not new SuccN (Zero). We start the implementation with a trait
change the value's representation. Among other things, we specifying the interface of natural numbers. For now, traits
want to guarantee that for each pair of types S <: T and can be seen as abstract classes; details are given later in
2
each instance x of S the following equality holds : Nat, natural
Section 6.2. According to the denition of trait
numbers provide two abstract methods isZero, and pred, as
x.asInstanceOf[T].asInstanceOf[S] = x well as three concrete methods succ, +, and -.
At the bottom of the type hierarchy are the two classes trait Nat {
scala.AllRef and scala.All. Type AllRef is a subtype of def isZero: boolean;
all reference types; its only instance is the null reference. def pred: Nat;
Since AllRef is not a subtype of value types, null is not a def succ: Nat = new Succ(this);
member of any such type. For instance, it is not possible to def + (x: Nat): Nat =
assign null to a variable of type int. if (x.isZero) this else succ + x.pred;
Type All is a subtype of every other type; there exist no def - (x: Nat): Nat =
instances of this type. Even though type All is empty, it is if (x.isZero) this else pred - x.pred;
nevertheless useful as a type parameter. For instance, the }
Scala library denes a value Nil of type List[All]. Because
lists are covariant in Scala, this makes Nil an instance of Note that Scala allows one to dene parameterless methods
List[T ], for any element type T . such as isZero, pred, and succ in class Nat. Such methods
The equality operation == between values is designed to are invoked every time their name is selected; no argument
be transparent with respect to the type's representation. For list is passed. Note also that abstract class members are
value types, it is the natural (numeric or boolean) equality. identied syntactically because they lack a denition; no
For reference types, == is treated as an alias of the equals additional abstract modier is needed for them.
method from java.lang.Object. That method is originally We now extend trait Nat with a singleton object Zero
dened as reference equality, but is meant to be overridden and a class for representing successors, Succ.
in subclasses to implement the natural notion of equality for
object Zero extends Nat {
these subclasses. For instance, the boxed versions of value
def isZero: boolean = true;
types would implement an equals method which compares
def pred: Nat = throw new Error("Zero.pred");
the boxed values. By contrast, in Java, == always means ref-
}
erence equality on reference types. While this is a bit more
class Succ(n: Nat) extends Nat {
ecient to implement, it also introduces a serious coherence
def isZero: boolean = false;
problem because boxed versions of equal values might no
def pred: Nat = n;
longer be equal (with respect to ==).
}
Some situations require reference equality instead of
user-dened equality. An example is hash-consing, where The Succ class illustrates a dierence between the class def-
eciency is paramount. For these cases, class AnyRef de- inition syntax of Scala and Java. In Scala, constructor pa-
nes an additional eq method, which cannot be overridden, rameters follow the class name; no separate class constructor
and is implemented as reference equality (i.e., it behaves like denition within the body of Succ is needed. This construc-
== in Java for reference types). tor is called the primary constructor ; the whole body of the
class is executed when the primary constructor is called at
3.2 Operations the time the class is instantiated. There is syntax for sec-
ondary constructors in case more than one constructor is
Another aspect of Scala's unied object model is that ev- desired (see Section 5.2.1 in [34]).
ery operation is a message send, that is, the invocation of The ability to have user-dened inx operators raises
a method. For instance the addition x + y is interpreted as the question about their relative precedence and associativ-
x.+(y), i.e. the invocation of the method + with x as the ity. One possibility would be to have xity-declarations in
receiver object and y as the method argument. This idea, the style of Haskell or SML, where users can declare these
which has been applied originally in Smalltalk, is adapted properties of an operator individually. However, such decla-
to the more conventional syntax of Scala as follows. First, rations tend to interact badly with modular programming.
2 asInstanceOf is Scala's standard type cast method dened in the
Scala opts for a simpler scheme with xed precedences and
root class Any. associativities. The precedence of an inx operator is deter-
3
scala.Any Subtype
View
scala.AnyVal scala.AnyRef
(java.lang.Object)
scala.Double
scala.Unit scala.ScalaObject
scala.Float scala.Boolean
scala.Iterable java.lang.String
scala.Long scala.Char
scala.Seq scala.Symbol
… (other Java classes)…
scala.Int
scala.Ordered
scala.List
scala.Byte scala.AllRef
scala.All
mined by its rst character; it coincides with the operator ||. Such operators can also be represented as methods be-
precedence of Java for those operators that start with an cause Scala allows to pass arguments by name. For instance,
operator character used in these languages. The following here is a user-dened trait Bool that mimics the pre-dened
lists operators in increasing precedence: booleans.
4
operation as an invocation of a method. In the interest of functions as arguments, or return them as results, are called
eciency, the Scala compiler translates operations on value higher-order functions.
types directly to primitive instruction codes; this, however, Once we have a function exists, we can use it to dene
is completely transparent to the programmer. a function forall by double negation: a predicate holds for
all values of an array if there does not exist an element for
3.3 Variables and Properties which the predicate does not hold. This is expressed by the
following function forall:
If every operation is a method invocation in Scala, what
about variable dereferencing and assignment? In fact, when def forall[T](xs: Array[T], p: T => boolean) = {
acting on class members these operations are also treated as def not_p(x: T) = !p(x);
method calls. For every denition of a variable var x: T in !exists(xs, not_p)
a class, Scala denes setter and getter methods as follows. }
5
Conversely, when a value of a function type is applied to def sqrts(xs: List[double]): List[double] =
some arguments, the type's apply method is implicitly in- xs filter (0 <=) map Math.sqrt;
serted. E.g. for p of type Function1[S , T ], the application
p(x) is expanded to p.apply(x). Note that Math.sqrt comes from a Java class. Such methods
can be passed to higher-order functions in the same way as
methods dened in Scala.
4.3 Rening Functions
Since function types are classes in Scala, they can be fur- 4.5 For Comprehensions
ther rened in subclasses. An example are arrays, which are
Scala oers special syntax to express combinations of cer-
treated as special functions over the integer domain. Class
Array[T] inherits from Function1[int, T], and adds meth- tain higher-order functions more naturally. For comprehen-
ods for array update and array length, among others:
sions are a generalization of list comprehensions found in
languages like Haskell. With a for comprehension the sqrts
package scala; function can be written as follows:
class Array[T] extends Function1[int, T] {
def apply(index: int): T = ...; def sqrts(xs: List[double]): List[double] =
def update(index: int, elem: T): unit= ...; for (val x <- xs; 0 <= x) yield Math.sqrt;
def length: int = ...;
Here, val x <- xs is a generator, which produces a sequence
def exists(p: T => boolean): boolean = ...;
of values, and 0 <= x is a lter, which eliminates some of
def forall(p: T => boolean): boolean = ...;
the produced values from consideration. The comprehension
...
returns another sequence formed from the values produced
}
by the yield part. There can be several generators and
Special syntax exists for function applications appearing on lters in a comprehension.
the left-hand side of an assignment; these are interpreted as For comprehensions are mapped to combinations involv-
update method. For instance, the assign-
applications of an ing the higher-order methodsmap, flatMap, and filter. For
ment a(i) = a(i) + 1 is interpreted as sqrts method above would
instance, the formulation of the
be mapped to the previous implementation of sqrts in Sec-
a.update(i, a.apply(i) + 1) . tion 4.4.
The power of for comprehensions comes from the fact
The interpretation of array accesses as function applications
that they are not tied to a particular data type. They
might seem costly. However, inlining transformations in the
can be constructed over any carrier type that denes ap-
Scala compiler transform code such as the one above to prim-
propriate map, flatMap, and filter methods. This includes
itive array accesses in the host system. 3
all sequence types , optional values, database interfaces, as
The above denition of the Array class also lists methods
well as several other types. Scala users might apply for-
exists and forall. Hence, it would not have been necessary
comprehensions to their own types, as long as these dene
to dene these operations by hand. Using the methods in
the required methods.
class Array, the hasZeroRow function can also be written as
For loops are similar to comprehensions in Scala.
follows.
They are mapped to combinations involving methods
def hasZeroRow(matrix: Array[Array[int]]) = foreach and filter. For instance, the for loop
matrix exists (row => row forall (0 ==)); for (val arg <- args) ... in Listing 1 is mapped to
Note the close correspondence of this code to a verbal spec- args foreach (arg => ...)
ication of the task: test whether in the matrix there exists
a row such that in the row all elements are zeroes. Note
also that we left out the type of the row parameter in the 5 Abstraction
anonymous function. This type can be inferred by the Scala
compiler from the type of matrix.exists. An important issue in component systems is how to abstract
from required components. There are two principal forms
4.4 Sequences of abstraction in programming languages: parameterization
and abstract members. The rst form is typically functional
Higher-order methods are very common when processing se- whereas the second form is typically object-oriented. Tra-
quences. Scala's library denes several dierent kinds of se- ditionally, Java supported functional abstraction for values
quences including lists, streams, and iterators. All sequence and object-oriented abstraction for operations. The new
types inherit from trait scala.Seq; and they all dene a set Java 1.5 with generics supports functional abstraction also
of methods which streamlines common processing tasks. For for types.
instance, the map method applies a given function uniformly Scala supports both styles of abstraction uniformly for
to all sequence elements, yielding a sequence of the function types as well as values. Both types and values can be pa-
results. Another example is the filter method, which ap- rameters, and both can be abstract members. The rest of
plies a given predicate function to all sequence elements and this section presents both styles and reviews at the same
returns a sequence of those elements for which the predicate time a large part of Scala's type system.
is true.
3 Arrays do not yet dene all of sequence methods, because some
The application of these two functions is illustrated in
of them require run-time types, which are not yet implemented
the following function, sqrts, which takes a list xs of double
precision numbers, and returns a list consisting of the square
roots of all non-negative elements of xs.
6
5.1 Functional Abstraction Variance. The combination of subtyping and generics in
a language raises the question how they interact. If C is
The following class denes cells of values that can be read
a type constructor and S is a subtype of T, does one also
and written.
have that C[S] is a subtype of C[T ]? Type constructors with
class GenCell[T](init: T) { this property are called covariant. The type constructor
private var value: T = init; GenCell should clearly not be covariant; otherwise one could
def get: T = value; construct the following program which leads to a type error
def set(x: T): unit = { value = x } at run time.
}
val x: GenCell[String] = new GenCell[String];
The class abstracts over the value type of the cell with the val y: GenCell[Any] = x; // illegal!
type parameter T. We also say, class GenCell is generic. y.set(1);
Like classes, methods can also have type parameters. val z: String = y.get
The following method swaps the contents of two cells, which
It is the presence of a mutable variable inGenCell which
must both have the same value type.
makes covariance unsound. GenCell[String] is
Indeed, a
def swap[T](x: GenCell[T], y: GenCell[T]): unit = { not a special instance of a GenCell[Any] since there are
val t = x.get; x.set(y.get); y.set(t) things one can do with a GenCell[Any] that one cannot do
} with a GenCell[String]; set it to an integer value, for in-
stance.
The following program creates two cells of integers and then On the other hand, for immutable data structures, co-
swaps their contents. variance of constructors is sound and very natural. For in-
stance, an immutable list of integers can be naturally seen
val x: GenCell[int] = new GenCell[int](1);
as a special case of a list of Any. There are also cases where
val y: GenCell[int] = new GenCell[int](2);
contravariance of parameters is desirable. An example are
swap[int](x, y)
output channels Chan[T], with a write operation that takes
Actual type arguments are written in square brackets; they a parameter of the type parameter T. Here one would like
replace the formal parameters of a class constructor or to have Chan[S ] <: Chan[T ] whenever T <: S .
method. Scala denes a sophisticated type inference sys- Scala allows to declare the variance of the type parame-
tem which permits to omit actual type arguments in both ters of a class using plus or minus signs. A + in front of a
cases. Type arguments of a method or constructor are in- parameter name indicates that the constructor is covariant
ferred from the expected result type and the argument types in the parameter, a − indicates that it is contravariant,
by local type inference [40, 38]. Hence, one can equivalently and a missing prex indicates that it is non-variant.
write the example above without any type arguments: For instance, the following trait GenList denes a simple
covariant list with methods isEmpty, head, and tail.
val x = new GenCell(1);
val y = new GenCell(2); trait GenList[+T] {
swap(x, y) def isEmpty: boolean;
def head: T;
def tail: GenList[T]
Parameter bounds. Consider a method updateMax which }
sets a cell to the maximum of the cell's current value and a
Scala's type system ensures that variance annotations are
given parameter value. We would like to dene updateMax so
sound by keeping track of the positions where a type pa-
that it works for all cell value types which admit a compar-
rameter is used. These positions are classied as covariant
ison function < dened in trait Ordered. For the moment
for the types of immutable elds and method results, and
assume this trait is dened as follows (a more rened version
contravariant for method argument types and upper type
of this trait is in the standard Scala library).
parameter bounds. Type arguments to a non-variant type
def < (x: T): boolean; ips between contra- and co-variant inside a type argument
7
} pression denotes instances of type GenList where the type
argument is an arbitrary subtype of T.
Note that the Empty object represents the empty list for all Covariant wildcards can be used in every type expression;
element types. Covariance makes this possible, since Empty's however, members where the type variable does not appear
type, GenList[All] is a subtype of GenList[T ], for any el- in covariant position are then forgotten in the type. This
ement type T. is necessary for maintaining type soundness. For instance,
the type GenCell<? extends Number> would have just the
Binary methods and lower bounds. So far, we have single member get of type Number, whereas the set method,
associated covariance with immutable data structures. In in which GenCell’s type parameter occurs contravariantly,
fact, this is not quite correct, because of binary methods. For would be forgotten.
instance, consider adding a prepend method to the GenList In an earlier version of Scala we also experimented with
trait. The most natural denition of this method takes an usage-site variance annotations similar to wildcards. At
argument of the list element type: rst-sight, this scheme is attractive because of its exibility.
A single class might have covariant as well as non-variant
trait GenList[+T] { ... fragments; the user chooses between the two by placing
def prepend(x: T): GenList[T] = // illegal! or omitting wildcards. However, this increased exibility
new Cons(x, this) comes at price, since it is now the user of a class instead of
}
its designer who has to make sure that variance annotations
are used consistently. We found that in practice it was quite
However, this is not type-correct, since now the type param-
dicult to achieve consistency of usage-site type annota-
eter T appears in contravariant position inside trait GenList.
tions, so that type errors were not uncommon. By contrast,
Therefore, it may not be marked as covariant. This is a pity
declaration-site annotations proved to be a great help in
since conceptually immutable lists should be covariant in
getting the design of a class right; for instance they provide
their element type. The problem can be solved by general-
excellent guidance on which methods should be generalized
izing prepend using a lower bound:
with lower bounds. Furthermore, Scala's mixin composi-
trait GenList[+T] { ... tion (see Section 6) makes it relatively easy to factor classes
def prepend[S >: T](x: S): GenList[S] = // OK into covariant and non-variant fragments explicitly; in Java's
new Cons(x, this) single inheritance scheme with interfaces this would be ad-
} mittedly much more cumbersome. For these reasons, later
versions of Scala switched from usage-site to declaration-site
prepend is now a polymorphic method which takes an ar- variance annotations.
gument of some supertype S of the list element type, T.
It returns a list with elements of that supertype. The
new method denition is legal for covariant lists since
5.2 Abstract Members
lower bounds are classied as covariant positions; hence the Object-oriented abstraction can be used in Scala as an al-
type parameter T now appears only covariantly inside trait ternative to functional abstraction. For instance, here is a
GenList. version of the cell type using object-oriented abstraction.
It is possible to combine upper and lower bounds in the
declaration of a type parameter. An example is the following abstract class AbsCell {
method less of class GenList which compares the receiver type T;
list and the argument list. val init: T;
private var value: T = init;
trait GenList[+T] { ... def get: T = value;
def less[S >: T <: Ordered[S]](that: List[S]) = def set(x: T): unit = { value = x }
!that.isEmpty && }
(this.isEmpty ||
this.head < that.head || The AbsCell class denes neither type nor value parameters.
this.head == that.head && Instead it has an abstract type member T and an abstract
this.tail less that.tail) value member init. Instances of that class can be created
} by implementing these abstract members with concrete def-
initions. For instance:
The method's type parameter S is bounded from below by
the list element type T and is also bounded from above by val cell = new AbsCell { type T = int; val init = 1 }
Ordered[S]. The lower bound is necessary to maintain co- cell.set(cell.get * 2)
variance of GenList. The upper bound is needed to ensure
The type of cell is AbsCell { type T = int }. Here,
that the list elements can be compared with the < operation.
the class type AbsCell is augmented by the renement
{ type T = int }. This makes the type alias cell.T = int
Comparison with wildcards. Java 1.5 also has a way to
known to code accessing the cell value. Therefore, type-
annotate variances which is based on wildcards [43]. The
specic operations such as the one below are legal.
scheme is essentially a syntactic variant of Igarashi and Vi-
roli's variant parametric types [26]. Unlike in Scala, in Java cell.set(cell.get * 2)
1.5 annotations apply to type expressions instead of type
declarations. As an example, covariant generic lists could be
expressed by writing every occurrence of the GenList type Path-dependent types. It is also possible to access
to match the form GenList<? extends T >. Such a type ex- AbsCell without knowing the binding of its type member.
8
For instance, the following method resets a given cell to its Without the singleton typethis.type, this would not have
initial value, independently of its value type. d.incr would be of type C, which does
been possible, since
not have a decr member. In that sense, this.type is similar
def reset(c: AbsCell): unit = c.set(c.init); to (covariant uses of ) Kim Bruce's mytype [9].
Why does this work? In the example above, the expres-
sion c.init has type c.T, and the method c.set has type Family polymorphism and self types. Scala's abstract
c.T => unit. Since the formal parameter type and the ar- type concept is particularly well suited for modeling fami-
gument type coincide, the method call is type-correct. lies of types which vary together covariantly. This concept
c.T is an instance of a path-dependent type. In gen- has been called family polymorphism. As an example, con-
eral, such a type has the form x1 . . . . .xn .t, where n > 0, sider the publish/subscribe design pattern. There are two
x1 , . . . , xn denote immutable values and t is a type member classes of participants subjects and observers. Subjects de-
of xn . Path-dependent types are a novel concept of Scala; ne a method subscribe by which observers register. They
their theoretical foundation is provided by the ν Obj calculus also dene a publish method which noties all registered
[35]. observers. Notication is done by calling a method notify
Path-dependent types rely on the immutability of the which is dened by all observers. Typically, publish is called
prex path. Here is an example where this immutability is when the state of a subject changes. There can be several
violated. observers associated with a subject, and an observer might
observe several subjects. The subscribe method takes the
var flip = false; identity of the registering observer as parameter, whereas an
def f(): AbsCell = { observer's notify method takes the subject that did the no-
flip = !flip; tication as parameter. Hence, subjects and observers refer
if (flip) new AbsCell { type T = int; val init = 1 } to each other in their method signatures.
else new AbsCell { type T = String; val init = "" } All elements of this design pattern are captured in the
} following system.
f().set(f().get) // illegal!
trait SubjectObserver {
In this example subsequent calls to f() return cells where type S <: Subject;
the value type is alternatingly int and String. The last type O <: Observer;
statement in the code above is erroneous since it tries to abstract class Subject: S {
set an int cell to a String value. The type system does private var observers: List[O] = List();
not admit this statement, because the computed type of def subscribe(obs: O) =
f().get would be f().T. This type is not well-formed, since observers = obs :: observers;
the method callf() is not a path. def publish =
for (val obs <- observers) obs.notify(this);
Type selection and singleton types. In Java, where }
classes can also be nested, the type of a nested class is trait Observer {
denoted by prexing it with the name of the outer class. def notify(sub: S): unit;
In Scala, this type is also expressible, in the form of }
Outer # Inner, where Outer is the name of the outer class }
in which class Inner is dened. The # operator denotes
The top-level trait SubjectObserver has two member
a type selection. Note that this is conceptually dierent
classes: one for subjects, the other for observers. The
from a path dependent type p.Inner, where the path p de-
Subject class denes methods subscribe and publish. It
notes a value, not a type. Consequently, the type expression
maintains a list of all registered observers in the private
Outer # t is not well-formed if t is an abstract type dened
variableobservers. The Observer trait only declares an
in Outer.
abstract method notify.
In fact, path dependent types in Scala can be expanded
Note that the Subject and Observer classes do not di-
to type selections. The path dependent type p.t is taken as a
rectly refer to each other, since such hard references would
shorthand for p.type # t. Here, p.type is a singleton type, prevent covariant extensions of these classes in client code.
which represents just the object denoted by p. Singleton
Instead,SubjectObserver denes two abstract types S and
types by themselves are also useful for supporting chaining
O which are bounded by the respective class types Subject
of method calls. For instance, consider a class C with a
and Observer. The subject and observer classes use these
method incr which increments a protected integer eld, and
abstract types to refer to each other.
a subclass D of C which adds a decr method to decrement
Note also that class Subject carries an explicit type an-
that eld.
notation:
class C {
class Subject: S { ...
protected var x = 0;
def incr: this.type = { x = x + 1; this } Here, S is called a self-type Subject. When a self-
of class
} type is given, it is taken as the type of this inside the class
class D extends C { (without a self-type annotation the type of this is taken as
def decr: this.type = { x = x - 1; this } usual to be the type of the class itself ). In class Subject, the
} self-type is necessary to render the call obs.notify(this)
type-correct.
Then we can chain calls to the incr and decr method, as in
Self-types can be arbitrary; they need not have a rela-
val d = new D; d.incr.decr; tion with the class being dened. Type soundness is still
9
guaranteed, because of two requirements: (1) the self-type more general than import clauses in Java. They can be used
of a class must be a subtype of the self-types of all its base anywhere, and can import members from of any object, not
classes, (2) when instantiating a class in a new expression, just from a package.
it is checked that the self type of the class is a supertype of
the type of the object being created. 5.3 Modeling Generics with Abstract Types
Self-types were rst introduced in the ν Obj calculus.
They are relatively infrequently used in Scala programs, but The presence of two type abstraction facilities in one lan-
they are nevertheless essential in situations where family guage raises the question of language complexity could we
polymorphism is combined with explicit references to this. have done with just one formalism? In this section we show
The mechanism dened in the publish/subscribe pattern that functional type abstraction ( aka generics) can indeed be
can be used by inheriting from SubjectObserver, dening modeled by object-oriented type abstraction ( aka abstract
application specic Subject and Observer classes. An ex- types). The idea of the encoding is as follows.
ample is the SensorReader object below that takes sensors Assume you have a parameterized class C with a type
as subjects and displays as observers. parameter t (the encoding generalizes straightforwardly to
multiple type parameters). The encoding has four parts,
object SensorReader extends SubjectObserver { which aect the class denition itself, instance creations of
type S = Sensor; the class, base class constructor calls, and type instances of
type O = Display; the class.
abstract class Sensor extends Subject {
val label: String; 1. The class denition of C is re-written as follows.
var value: double = 0.0;
class C {
def changeValue(v: double) = {
type t;
value = v;
/* rest of class */
publish;
}
}
} That is, parameters of the original class are modeled
abstract class Display extends Observer { as abstract members in the encoded class. If the type
def println(s: String) = ... parameter t has lower and/or upper bounds, these carry
def notify(sub: Sensor) = over to the abstract type denition in the encoding.
println(sub.label + " has value " + sub.value); The variance of the type parameter does not carry over;
} variances inuence instead the formation of types (see
} Point 4 below).
In this object, type S is bound to Sensor whereas type O is 2. Every instance creation new C [T ] with type argument
bound to Display. Hence, the two formerly abstract types T is rewritten to:
are now dened by overriding denitions. This tying the
knot is always necessary when creating a concrete class in- new C { type t = T }
stance. On the other hand, it would also have been possible
to dene an abstract SensorReader class which could be re- 3. If C [T ] appears as a superclass constructor, the inher-
ned further by client code. In this case, the two abstract iting class is augmented with the denition
10
conicts between abstract type names that emulate type pa- single inheritance; i.e., programmers can specialize classes
rameters. Second, generics and abstract types usually serve Buffer with additional meth-
by subclassing. To enrich class
distinct roles in Scala programs. Generics are typically used ods forall and exists, we could, for instance, create a sub-
when one needs just type instantiation, whereas abstract class of IterableBuffer dening the new functionality:
types are typically used when one needs to refer to the ab-
stract type from client code. The latter arises in particular
class IterableBuffer[T] extends Buffer[T] {
in two situations: One might want to hide the exact deni-
def forall(p: T => Boolean): Boolean = {
tion of a type member from client code, to obtain a kind of
val it = elements; var res = true;
encapsulation known from SML-style module systems. Or
while (res && it.hasNext) { res = p(it.next) }
res
one might want to override the type covariantly in subclasses
}
to obtain family polymorphism.
def exists(p: T => Boolean): Boolean = {
Could one also go the other way, encoding abstract types
val it = elements; var res = false;
with generics? It turns out that this is much harder, and
while (!res && it.hasNext) { res = p(it.next) }
that it requires at least a global rewriting of the program.
res
This was shown by studies in the domain of module sys-
}
tems where both kinds of abstraction are also available
}
[27]. Furthermore in a system with bounded polymorphism,
this rewriting might entail a quadratic expansion of type
bounds [8]. In fact, these diculties are not surprising if one Mixin-class composition The problem with the code
considers the type-theoretic foundations of both systems. above is its limited potential for reuse. Imagine there is an
Generics (without F-bounds) are expressible in System F<: independent extension of class Buffer which models stacks:
[11] whereas abstract types require systems based on depen-
class Stack[T] extends Buffer[T] {
dent types. The latter are generally more expressive than
def push(elem: T): Unit = add(elem);
the former; for instance ν Obj with its path-dependent types def pop: T = { val y = xs.head; xs = xs.tail; y }
can encode F<: . }
def next: T = { are not present in the new context, yielding a method not
}
} Ambiguities In Scala, every class inherits exactly from
} one superclass and acquires class members from multiple
other classes via mixin-based class composition. Imagine for
The implementation of class Buffer relies on the following instance the following subclass of Buffer which introduces
iterator abstraction:
a method sameElements together with an internally used
trait Iterator[T] { forall method.
def hasNext: Boolean; class ComparableBuffer[T] extends Buffer[T] {
def next: T; def forall(p: T => Boolean): Boolean = {
} val it = elements; var res = true;
while (res && it.hasNext) { res = p(it.next) }
Inheritance Like most mainstream object-oriented lan- res
guages, Scala's primary class reuse mechanism is based on }
11
def sameElements(b: IterableBuffer[T]): Boolean = val it = elements; var res = true;
forall(elem => b.exists(elem.equals)); while (res && it.hasNext) { res = p(it.next) }
} res
}
MyStack oering functional-
We could derive a stack class def exists(p: T => Boolean): Boolean = {
ity provided by both IterableBuffer and ComparableBuffer val it = elements; var res = false;
by using the two classes as mixins: while (!res && it.hasNext) { res = p(it.next) }
res
class MyStack[T] extends Stack[T]
}
with IterableBuffer[T]
}
with ComparableBuffer[T]; // error!
Trait Iterable denes methods forall and exists as be-
In Scala, methods dened in mixins either represent new
methods or they override the respective methods in the ac-
fore, but defers the denition of method elements it
tual superclass. As the previous example shows, it may
is abstract in the terminology of Java. As opposed to
class IterableBuffer, this trait can be mixed into all
happen that two mixins dene the same method. For class
classes. If Iterable is mixed into a class without a concrete
MyStack it is unclear which forall method to use. This
elements method, then the resulting class will have a de-
ambiguity constitutes a compile-time error which has to be
ferred elements method, otherwise, the concrete method will
resolved by the programmer explicitly. A possible solution
implement the deferred method mentioned in trait Iterable.
is to introduce a new forall method which forwards the
Thus, concrete methods always override abstract ones in
call to the desired implementation. The following program
mixin-class compositions. This principle is exploited in the
makes use of the super[C] primitive, which allows one to
following alternative denition of class IterableBuffer:
refer to concrete denitions in the mixin class C:
class IterableBuffer[T] extends Buffer[T]
class MyStack[T] extends Stack[T]
with Iterable[T];
with IterableBuffer[T]
with ComparableBuffer[T] {
override def forall(p: T => Boolean) =
super[IterableBuffer].forall(p);
6.3 Layering Classes and Traits
} Scala's mixin-class composition mechanism makes it easy to
compose complex classes with extensive functionality from
smaller and simpler ones that model only particular aspects.
6.2 Traits The previous section showed how traits can be used to create
generic abstractions that can be used to add new methods
Besides ambiguities, another serious problem of multiple
or to implement deferred methods of existing classes. Traits
inheritance is the diamond inheritance dilemma which ap-
mixed into a class can also override existing methods, as the
pears if a class inherits from two other classes that share su-
following code fragment shows:
perclasses. Without further restrictions, these superclasses
would get inherited twice in this scenario. This would lead to trait Displayable[T] {
a duplication of the state encapsulated by these superclasses def elements: Iterator[T];
and therefore would result in serious consistency issues. override def toString(): String = {
To avoid this, Scala allows a class to be mixed into an- val res = new StringBuffer;
other class only if it has not been used before in the other for (val elem <- elements) res.append(" " + elem);
class as either superclass or mixin. Unfortunately, this rule res.toString()
is very restrictive, ruling out many cases where inheriting }
twice from the same class would not constitute a problem }
this is the case in particular for classes that do not en- class DisplayableBuffer[T] extends IterableBuffer[T]
capsulate state (interfaces in Java fall into that category). with Displayable[T];
For this reason, Scala introduces the notion of traits. Traits
are abstract classes that do not encapsulate state, neither Class DisplayableBuffer[T] now provides the toString()
in form of variable denitions nor by providing a construc- method dened in trait Displayable[T].
tor with parameters. Opposed to interfaces in Java though, The presented technique for overriding methods only
they may implement concrete methods. works if the method in the superclass or in the mixed in
Since traits do not encapsulate state, inheriting twice traits is concrete. But often, one would like to dene generic
from a trait is legal in Scala. It is therefore possible to have traits that enhance an existing method by overriding with-
the same trait multiple times in the superclass hierarchy of out that method being concrete in the superclass. As an
a class. example, consider providing a building block for making it-
Reuse of class IterableBuffer is also restricted by erators synchronized such that they can be used in a con-
the requirement that it can only be mixed into classes current setting. A naive denition would be:
that Buffer.
subclass But the functionality provided
// erroneous trait definition
by IterableBuffer only depends on the existence of an
trait SynchronizedIterator[T] extends Iterator[T] {
elements method. Scala makes it possible to express this
override def next: T = synchronized { super.next }
in the following form:
override def hasNext: Boolean =
trait Iterable[T] { synchronized { super.hasNext }
def elements: Iterator[T]; }
def forall(p: T => Boolean): Boolean = {
12
This denition is illegal, because supertrait Iterator does the provided services, deferred members can be seen as the
not provide concrete implementations for both methods next required services. The composition of components is based
and hasNext. Thus, the super reference is not allowed. on mixins, which allow programmers to create bigger com-
Scala still allows programmers to dene such an abstrac- ponents from smaller ones.
tion. However, it requires that an abstract modier is used The mixin-class composition mechanism of Scala iden-
for all those methods which override abstract methods in ties services with the same name; for instance, a de-
the static superclass, yet which are supposed to override ferred method m can be implemented by a class C den-
concrete methods in a mixin composition. ing a concrete method m simply by mixing-in C. Thus,
the component composition mechanism associates automat-
trait SynchronizedIterator[T] extends Iterator[T] { ically required with provided services. Together with the
abstract override def next: T = rule that concrete class members always override deferred
synchronized { super.next } ones, this principle yields recursively pluggable components
abstract override def hasNext: Boolean = where component services do not have to be wired explic-
synchronized { super.hasNext }
itly [47].
}
This approach simplies the assembly of large compo-
Classes containing methods tagged with both abstract and nents with many recursive dependencies. It scales well even
override cannot be instantiated they have to be declared in the presence of many required and provided services, since
abstract themselves. Furthermore, such classes can only the association of the two is automatically inferred by the
be mixed into classes that provide concrete versions for all compiler. The most important advantage over traditional
mixed in methods agged with abstract and override. black-box components is that components are extensible en-
Traits like SynchronizedIterator are extremely useful tities: they can evolve by subclassing and overriding. They
for synchronizing arbitrary iterator implementations simply can even be used to add new services to other existing com-
by a mixin composition. For instance, we could implement ponents, or to upgrade existing services of other compo-
a synchronized iterator for theBuffer class based on an nents. Overall, these features enable a smooth incremental
13
7.2 Pattern Matching Over Class Hierarchies guard x == y.4
14
8.3 Regular Sequence Pattern Matching 9 Autonomous Components
XML nodes can be decomposed using pattern matching.
The Scala language as such does not provide any primi-
Scala allows to use XML syntax here too, albeit only to
tives for concurrent programming. Instead the core lan-
match elements. The following example shows how to add
guage has been designed to make it easy to build libraries
an entry to a phonebook element. to provide dierent concurrency models built on top of the
thread model of the underlying host language. In this sec-
import scala.xml.Node ;
tion we will exemplify the power of Scala by implementing
def add(phonebook: Node, newEntry: Node): Node =
a small library for fault-tolerant active objects with a avor
phonebook match {
of Erlang-like actors [1].
case <phonebook>{ cs @ _* }</phonebook> =>
<phonebook>{ cs }{ newEntry }</phonebook> Imagine for example that you have written a server class
15
message passing, e.g., by implementing an Erlang like actor
model. In Erlang each actor (or process in the Erlang ter- class Signal extends Message;
minology) has a mailbox to which other processes can asyn-
case class Normal() extends Signal;
chronously send messages. The process owning the mailbox
case class Exit(p: Process, m: Message)
can selectively receive messages from the mailbox. In Scala
extends Message;
we can implement mailboxes with the following signature.
class Process(def body: unit) extends Thread
class MailBox { with MailBox {
def send(msg: Any): unit; private var exitReason: AnyRef = null;
def receive[a](f: PartialFunction[Any, a]): a; private var links: List[Process] = Nil;
def receiveWithin[a](msec: long) override def run() =
(f: PartialFunction[Any, a]): a; try { body; signal(Normal()) }
} catch {
case _: InterruptedException =>
Messages are added to the mailbox by the send method. signal(exitReason);
Messages are removed using the receive method, which is case exitSignal =>
passed a message processor f as argument, which is a par- signal(exitSignal);
tial function from messages to some arbitrary result type. }
Typically, this function is implemented as a pattern match- private def signal(s: Message) =
ing expression. The receive method blocks until there is a links.foreach(
message in the mailbox for which its message processor is p: Process => p.send(Exit(this, s)));
dened. The matching message is then removed from the def !(msg: Message) = send(msg);
mailbox and the blocked thread is restarted by applying the def link(p: Process) = links = p :: links;
message processor to the message. Both sent messages and def unlink(p: Process) =
receivers are ordered in time. A receiver r is applied to a links = links.remove(p2 => p == p2);
matching message m only if there is no other (message, re- def spawnLink(def body: unit) = {
ceiver) pair which precedes (m, r) in the partial ordering on val p = new Process(body);
pairs that orders each component in time. p.link(this); p.start(); p
We can now extend our Process class by mixing in the }
MailBox class. def self = this;
def exit(reason: AnyRef): unit = {
class Process(def body: unit) extends Thread exitReason = reason; interrupt()
with MailBox { }
//... }
}
Listing 2: The Process class.
In order to build fault-tolerant systems it is imperative
that we can detect failures in a process. This can be achieved
by making it possible to link processes. When a process (A)
is linked to another process (B), A will send a signal to B
client of this library might wish to treat such lists as sets,
when A dies. This makes it possible to monitor the failure
supporting operations such as member inclusion or contain-
of processes and to implement supervision trees where a su-
ment tests. However, the provider of the class might not
pervisor process monitors worker processes and can restart
have thought of this usage scenario, and consequently might
them if they fail.
have left out these methods from the interface of GenList.
To implement this in Scala we have to add a list of links
One might argue that inheritance can allow clients to tai-
to the Process class and provide the link methods, as well
lor the supported methods of a class to their requirements;
as signal a failure to all linked processes. We can now give
however this is only true if a client has control over all cre-
the complete Process class, see Listing 2.
ation sites of the class. If the library also returns an opera-
We can use the Process class to implement a small
tion such as
counter server (see Listing 3). This server implements a
counter that can be incremented and read by sending the def fromArray(xs: Array[T]): GenList[T]
messages Increment, and GetValue respectively. The server
itself consists of only one method, the server method. The then inheritance cannot be used to turn a GenList into a
object Counter provides a functional interface to the counter SetList after it has been returned from method fromArray.
process. One can circumvent this restriction to some degree by in-
cluding factory methods [20]in libraries. However, this in-
volves fairly complicated frameworks which are dicult to
10 Component Adaptation learn and instantiate, and it fails for library components that
inherit from classes that need to be extended by clients.
Even component systems with powerful constructs for ab-
This unsatisfactory situation is commonly called the ex-
straction and composition face a problem when it comes
ternal extensibility problem. It has been argued that this
to integrating sub-systems developed by dierent groups at
problem holds back the development of software components
dierent times. The problem is that the interface of a com-
to a mature industry where components are independently
ponent developed by one group is often not quite right for
manufactured and deployed [28].
clients who wish to use that component. For instance, con-
sider a library with a class like GenList from Section 5. A
16
when a member selected from e is not a member of T. For
object Counter { instance, assume a value xs of type List[T] which is used
class Messages(); in the following two lines.
case class Increment() extends Messages;
case class GetValue(from: Process) extends Messages; val s: Set[T] = xs;
case class Stop() extends Messages; xs contains x
case class Value(value: int) extends Messages;
def start: Process = spawn(server(0)); The compiler would insert applications of the view dened
def increment(Counter: Process): unit = above into these lines as follows:
Counter ! Increment();
val s: Set[T] = view(xs);
def value(Counter: Process): int = {
view(xs) contains x
Counter ! GetValue(self);
receive { case Value(value) => value } Which views are available for insertion? Scala considers as
} candidates all views which can be accessed at the point of
def stop(Counter: Process): unit = Counter ! Stop(); insertion without a prex expression. This includes views
private def server(v: int): unit = { dened locally or in some enclosing scope, as well as views
var stop = false; inherited from base classes or imported from other objects
var value = v; by an import clause. Shadowing is not taken into account.
while (! stop) {
That is, a local view does not hide a view dened in an en-
receive {
closing scope. A view is applicable if can be applied to the ex-
case Increment() => value = value + 1;
pression and it maps to the desired type (or to any type con-
case GetValue(from) => from ! Value(value);
taining the desired member). Among all candidates, Scala
case Stop => stop = true;
picks the most specic applicable view. Here, specicity is
}
interpreted in the same way as for overloading resolution in
}
Java and Scala. It is an error if no view is applicable, or
}
among the applicable views no most specic one exists.
}
Locality is ensured by the restriction that only those
Listing 3: Example of the use of processes, a simple server. views accessible without a prex are candidates. Clients
can tailor the set of available views by selectively importing
objects dening views.
Views are used frequently in the Scala library to upgrade
Java's types to support new Scala traits. An example is
10.1 Views Scala's trait Ordered which denes a set of comparison op-
Scala introduces a new concept to solve the external exten- erations. Views from all basic types as well as class String
sibility problem: views allow one to augment a class with to this type are dened in a module scala.Predef. Since the
new members and supported traits. Views follow some of members of this module are imported implicitly into every
the intuitions of Haskell's type classes, translating them into Scala program, the views are always available. From a user's
an object-oriented approach. Unlike with type classes, the perspective, it is almost as if the Java classes are augmented
scope of a view can be controlled, and competing views can by the new traits.
coexist in dierent parts of one program.
A view is introduced by a normal Scala method denition 10.2 View Bounds
which denes an entity named view. For instance, assume
the following trait for simple generic sets: As presented so far, view methods have to be visible stati-
cally at the point of their insertion. Views become even more
trait Set[T] { useful if one can abstract over the concrete view method to
def include(x: T): Set[T]; be inserted. An example is the following generic maximum
def contains(x: T): boolean method, which returns the maximum element of a non-
} empty list.
A view from class GenList to class Set is introduced by the def maximum[T <% Ordered[T]](xs: List[T]): unit = {
following method denition. var mx = xs.head;
for (val x <- xs.tail) if (mx < x) mx = x
def view[T](xs: GenList[T]): Set[T] = new Set[T] { mx
def include(x: T): Set[T] = }
x prepend xs;
def contains(x: T): boolean = The method has a view bounded type parameter
!isEmpty && (xs.head == x || xs.tail contains x) [T <% Ordered[T]]. This type parameter can be instan-
} tiated to any type T which is a subtype of, or viewable as
Ordered[T ]. In particular, we can apply maximum to lists of
Hence, if xs is a GenList[T ], then view(xs) would return a
basic types for which standard Ordered views exist.
Set[T ].
Note that a view method application needs to be inserted
The only dierence with respect to a normal method
in the mx < x condition in method maximum. Where does this
denition is that views are inserted automatically by the
view method come from? Since it is not statically known at
Scala compiler. Say, e is an expression of type T. A view
the point of insertion, it must be passed as a parameter.
is implicitly applied to e in one of two possible situations:
In fact, for every view-bounded type parameter [t <: T ],
when the expected type of e is not (a supertype of ) T, or
an implicit value parameter (view: t => T ) is added to the
17
parameter list of a class or method. When the class con- view resolution
structor or method is called, a concrete view method which
matches the view parameter type is passed. The selection type inference
18
11.5 Pattern Matching Expressions be illegal in the resulting top-level class. To avoid them, a
new package-private access member is added to the enclosing
Pattern matching expressions are translated into automata
class for each of its private members that is referenced by an
such that the number of tests needed to nd the rst match-
nested class and all references in nested classes to these pri-
ing pattern is minimized. Algebraic patterns as discussed
vate members are replaced by references the corresponding
in Section 7 and simple sequence patterns are translated
access members.
with the technique used by the extensible Java compiler
JaCo [49, 46]. The translation scheme corresponds closely
11.8 Mixin Expansion
to the one introduced by the Pizza compiler [36].
The more powerful regular expression patterns discussed In Scala, every class may contain code and every class may
in Section 8.3 use a dierent translation scheme which is be used as a mixin. Therefore, by using mixins, it is possible
based on the theory of regular tree grammars. to dene classes that inherit code from several other classes.
In the following example
11.6 Local Classes and Functions
trait A {
Both Scala and Java support local classes (i.e., classes de- def foo: String;
ned in the body of a function) and Scala also supports local def bar: String;
functions. These local denitions are eliminated by lifting }
them out into the next enclosing class; local classes become class B extends A {
new private classes of the enclosing class and local functions def foo: String = "foo";
become new private methods. }
The main diculty of lifting denitions is the possible class M extends A {
presence of references to variables of the enclosing function def bar: String = "bar";
in the body of the lifted denition. These references become }
invalid after the lifting because the referenced variables are class C extends B with M;
no longer in the scope of the lifted denition. This is solved
by adding to the lifted denition a new argument for every
the class C inherits the implementation of method foo from
referenced variable of the enclosing function and by replac-
class B and the implementation of method bar from class M.
The JVM supports only single class inheritance. There-
ing each reference to one of these variables by a reference to
fore, code inheritance from multiple classes has to be simu-
the corresponding new argument.
lated. This is done by copying the code that can't be inher-
This solution works well as long as all referenced vari-
ited.
ables are immutable. This is the case in Java but not in
In addition to class inheritance, the JVM supports mul-
Scala. With mutable variables, the described solution fails
tiple interface inheritance. An interface is an abstract class
because changes that occur after the lifted class is created or
that only declares members but contains no code. This
the lifted function is called will never be noticed. Further-
makes it possible to replicate any Scala type hierarchy on
more, there is no way to modify the value of the variable
the JVM.
from within the lifted denition. T
To do so, every class C is split into a trait C and a
To overcome these problems, mutable variables refer- C T
class C . The trait C contains a member declaration for
enced by local denitions are rst transformed into Cells.
For example the variable denition
every member declared in class C and, assuming that class
C extends class S and mixins M0 , ..., Mn , it extends the
T T T C
var i: Int = 0; traits S and M0 , ..., Mn . The class C extends the class
C T
S and the trait C and receives all the code from class C .
would be transformed into In addition to that, all the code from the mixins M0 , ..., Mn
C
is duplicated in class C .
val i: Cell[Int] = new Cell(0);
The code below shows how the example is transformed.
T
if it was referenced by a local denition. Note that C keeps the name of C and that the name of
C C is obtainend by appending $class to the name C .
11.7 Inner Classes trait A {
Although Java supports inner and nested classes, the JVM
def foo: String;
supports neither of them. Therefore Java compilers have
def bar: String;
}
to transform those classes into top-level classes. The Scala
abstract class A$class with A;
compiler uses techniques similar to those used by Java com-
trait B with A {
pilers.
def foo: String;
Inner classes are transformed into nested classes by
}
adding a new eld containing a reference to the enclosing
class B$class extends A$class with B {
class instance and by replacing all references to this instance
def foo: String = "foo";
by references to this new eld. All constructors are also aug-
}
mented with a new argument needed to initialize the new
trait M with A {
eld.
def bar: String;
The transformation of nested classes into top-level classes
}
involves giving them a non-conicting top-level name. The
class M$class extends A$class with M {
only diculty is the possible presence of references to pri-
def bar: String = "bar";
vate members of the enclosing class. These references would
}
19
trait C with B with M; has been formalized by Igarashi, Pierce, and Wadler in [25].
class C$class extends B$class with C { They also prove that all added type casts are safe (they
def bar: String = "bar"; never raise a ClassCastException).
}
20
toString from a Scala program. Furthermore, dening a get { return d; }
toString method in a Scala class will eectively override set { if (value >= -273) d = value; }
System.Object.ToString. To avoid confusion, especially }
among .NET programmers, the compiler will reject any at- }
tempt to override any of these methods under its original
The value of a property is obtained using its name (as
System.Object name.
with a eld); to set a new value, the eld assignment syntax
trait A { is used.
// ok, overrides System.Object.ToString
override def toString() = "A"; Celsius c = new Celsius(); c.degree = c.degree - 1;
21
Structures. Value types are useful for representing Of course, Scala adopts a large part of the concepts and
lightweight objects. In C# they are called structures and syntactic conventions of Java [22] and C# [14]. Scala's way
are dened using special syntax. to express properties is loosely modelled after Sather [42].
From Smalltalk [21] comes the concept of a uniform ob-
struct Point { ject model. From Beta [30] comes the idea that everything
public Point(int x, int y) { this.x = x; this.y = y; } should be nestable, including classes. Scala's design of mix-
public int x, y; ins comes from object-oriented linear mixins [6], but denes
} mixin composition in a symmetric way, similar to what is
found in mixin modules [13, 24, 48] or traits [41]. Scala's
Once dened in a .NET assembly, structures can be used
abstract types have close resemblances to abstract types of
in Scala programs like regular reference types.
signatures in the module systems of ML [23] and OCaml [29],
def r(p: Point): double = Math.Sqrt(p.x*p.x + p.y*p.y); generalizing them to a context of rst-class components.
def dist(p1: Point, p2.Point): double = { For-comprehensions are based on Haskell's monad compre-
val p = new Point(p1.x - p2.x, p1.y - p2.y); hensions [44], even though their syntax more closely resem-
r(p) bles XQuery [3]. Views have been inuenced by Haskell's
} type classes [45]. They can be seen as an object-oriented
version of parametric type classes [37], but they are more
general in that instance declarations can be local and are
Enumerations. .NET has native support for type-safe scoped. Classboxes [2] provide the key benets of views in
enumerations. An enumeration type extends System.Enum a dynamically typed system. Unlike views, they also permit
and denes a value type, since System.Enum extends local rebinding so that class extensions can be selected using
System.ValueType. The members of an enumeration are dynamic dispatch.
named constants of any integral type (int, short, etc.) ex- In a sense, Scala represents a continuation of the work on
cept for char. Every enumeration denes a distinct type and Pizza [36]. Like Pizza, Scala compiles to the JVM, adding
its members cannot be used as values of the enumeration's higher-order functions, generics and pattern matching, con-
underlying type; this is only possible with an explicit cast. structs which have been originally developed in the func-
In C#, an enumeration is dened using special syntax. tional programming community. Whereas Pizza is back-
wards compatible with Java, Scala's aim is only to be in-
public enum Color {
teroperable, leaving more degrees of freedom in its design.
Red, Green, Blue
Scala's aim to provide advanced constructs for the ab-
}
straction and composition of components is shared by sev-
When a .NET enumeration is used in a Scala program, eral recent research eorts. Abstract types are a more con-
it is treated as a reference type. Its members are seen as servative construction to get most (but not all) of the ben-
static elds that have the type of the enumeration. ets of virtual classes in gbeta [16, 17]. Closely related are
also the delegation layers in FamilyJ [39] and work on nested
class Color extends System.Enum; inheritance for Java [32]. Jiazzi [31] is an extension to Java
object Color { that adds a module mechanism based on units, a powerful
val Red: Color; form of parametrized module. Jiazzi supports extensibility
val Green: Color; idioms similar to Scala, such as the ability to implement
val Blue: Color; mixins.
} The Nice programming language [4] is a recent object-
oriented language that is similar to Java, but has its her-
In a .NET assembly these elds are represented as lit- itage in ML≤ [5]. Nice includes multiple dispatch, open
erals. Literals have xed values which reside in the assem-
classes, and a restricted form of retroactive abstraction
bly metadata, and they cannot be referenced at runtime.
based on abstract interfaces. Nice does not support modular
Instead, the compiler inlines the value associated with the
implementation-side typechecking. While Nice and Scala are
eld.
languages which dier signicantly from Java, they both are
Every enumeration type is augmented with methods that
designed to interoperate with Java programs and libraries,
perform comparisons (==, !=, <, <=, >, >=) and bitwise logical
and their compiler targets the JVM.
operations (|, &, ^).
MultiJava [12] is a conservative extension of Java that
def isRed(c: Color): Boolean = (c == Color.Red); adds symmetric multiple dispatch and open classes. It pro-
vides alternative solutions to many of the problems that
However, such methods do not exist in the denition of Scala also addresses. For instance, multiple dispatch pro-
the enumeration. They are recognized by the compiler and vides a solution to the binary method problem, which is
implemented to perform the respective primitive operation addressed by abstract types in Scala. Open classes provide
on the numerical values associated with the members of the a solution to the external extensibility problem, which is
enumeration. solved by views in Scala. A feature only found in Multi-
Java is the possibility to dynamically add new methods to
13 Related Work
a class, since open classes are integrated with Java's regular
dynamic loading process. Conversely, only Scala allows to
delimit the scope of an external class extension in a program.
Scala's design is inuenced by many dierent languages and
OCaml and Moby[19] are two alternative designs that
research papers. The following enumeration of related work
combine functional and object-oriented programming using
lists the main design inuences.
static typing. Unlike Scala, these two languages start with
a rich functional language including a sophisticated module
22
system and then build on these a comparatively lightweight [3] S. Boag, D. Chamberlin, M. F. Fermandez, D. Flo-
mechanism for classes. rescu, J. Robie, and J. Simon. XQuery 1.0:
An XML Query Language. W3c recommenda-
tion, World Wide Web Consortium, November 2003.
14 Conclusion http://www.w3.org/TR/xquery/.
23
[20] E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design [38] M. Odersky, C. Zenger, and M. Zenger. Colored Local Type
Patterns: Elements of Reusable Object-Oriented Software. Inference. In Proceedings of the 28th ACM Symposium on
Addison Wesley, Massachusetts, 1994. Principles of Programming Languages, pages 4153, Lon-
don, UK, January 2001.
[21] A. Goldberg and D. Robson. Smalltalk-80: The Language
and Its Implementation. Addison-Wesley, 1983. [39] K. Ostermann. Dynamically Composable Collaborations
with Delegation Layers. In Proceedings of the 16th Euro-
[22] J. Gosling, B. Joy, G. Steele, and G. Bracha. The Java Lan- pean Conference on Object-Oriented Programming, Malaga,
guage Specication. Java Series, Sun Microsystems, second Spain, 2002.
edition, 2000.
[40] B. C. Pierce and D. N. Turner. Local Type Inference. In
[23] R. Harper and M. Lillibridge. A Type-Theoretic Approach Proc. 25th ACM Symposium on Principles of Programming
to Higher-Order Modules with Sharing. In Proc. 21st ACM Languages, pages 252265, New York, NY, 1998.
Symposium on Principles of Programming Languages, Jan-
uary 1994. [41] N. Schärli, S. Ducasse, O. Nierstrasz, and A. Black. Traits:
Composable Units of Behavior. In Proceedings of the
[24] T. Hirschowitz and X. Leroy. Mixin Modules in a Call-by- 17th European Conference on Object-Oriented Program-
Value Setting. In European Symposium on Programming, ming, Darmstadt, Germany, June 2003.
pages 620, 2002.
[42] D. Stoutamire and S. M. Omohundro. The Sather 1.0 Spec-
[25] A. Igarashi, B. Pierce, and P. Wadler. Featherweight Java: ication. Technical Report TR-95-057, International Com-
A minimal core calculus for Java and GJ. In Proceedings of puter Science Institute, Berkeley, 1995.
the Conference on Object-Oriented Programming, Systems,
Languages & Applications, volume 34(10), pages 132146, [43] M. Torgersen, C. P. Hansen, E. Ernst, P. vod der Ahé,
1999. G. Bracha, and N. Gafter. Adding Wildcards to the Java
Programming Language. In Proceedings SAC 2004, Nicosia,
[26] A. Igarashi and M. Viroli. Variant Parametric Types: A Cyprus, March 2004.
Flexible Subtyping Scheme for Generics. In Proceedings of
the Sixteenth European Conference on Object-Oriented Pro- [44] P. Wadler. The Essence of Functional Programming. In
gramming (ECOOP2002), pages 441469, June 2002. Proc.19th ACM Symposium on Principles of Programming
Languages, pages 114, January 1992.
[27] M. P. Jones. Using parameterized signatures to express mod-
ular structure. In Proceedings of the 23rd ACM Sympo- [45] P. Wadler and S. Blott. How to make ad-hoc Polymorphism
sium on Principles of Programming Languages, pages 68 less ad-hoc. In Proc. 16th ACM Symposium on Principles
78. ACM Press, 1996. of Programming Languages, pages 6076, January 1989.
[28] R. Keller and U. Hölzle. Binary Component Adaptation. In [46] M. Zenger. Erweiterbare Übersetzer. Master's thesis, Uni-
Proceedings ECOOP, Springer LNCS 1445, pages 307329, versity of Karlsruhe, August 1998.
1998.
[47] M. Zenger. Type-Safe Prototype-Based Component Evolu-
[29] X. Leroy. Manifest Types, Modules and Separate Compila- tion. In Proceedings of the European Conference on Object-
tion. In Proc. 21st ACM Symposium on Principles of Pro- Oriented Programming, Málaga, Spain, June 2002.
gramming Languages, pages 109122, January 1994.
[48] M. Zenger. Programming Language Abstractions for Ex-
[30] O. L. Madsen and B. Moeller-Pedersen. Virtual Classes - A tensible Software Components. PhD thesis, Department of
Powerful Mechanism for Object-Oriented Programming. In Computer Science, EPFL, Lausanne, March 2004.
Proc. OOPSLA'89, pages 397406, October 1989.
[49] M. Zenger and M. Odersky. Extensible Algebraic Datatypes
[31] S. McDirmid, M. Flatt, and W. Hsieh. Jiazzi: New-age with Defaults. In Proceedings of the International Confer-
Components for Old-Fashioned Java. In Proc. of OOPSLA, ence on Functional Programming, Firenze, Italy, September
October 2001. 2001.
[32] N. Nystrom, S. Chong, and A. Myers. Scalable Extensibility
via Nested Inheritance. In Proc. OOPSLA, Oct 2004.
[33] Oasis. RELAX NG. See http://www.oasis-open.org/.
[34] M. Odersky and al. The Scala Language Specication. Tech-
nical report, EPFL Lausanne, Switzerland, Jan. 2004. Avail-
able online http://scala.epfl.ch.
[35] M. Odersky, V. Cremet, C. Röckl, and M. Zenger. A
nominal theory of objects with dependent types. In Proc.
ECOOP'03, Springer LNCS 2743, July 2003.
[36] M. Odersky and P. Wadler. Pizza into Java: Translating
theory into practice. In Proc. 24th ACM Symposium on
Principles of Programming Languages, pages 146159, Jan-
uary 1997.
[37] M. Odersky, P. Wadler, and M. Wehr. A Second Look at
Overloading. In Proc. ACM Conf. on Functional Program-
ming and Computer Architecture, pages 135146, June 1995.
24