types would implement an
equals
method which compares
the boxed values. By contrast, in Java,
==
always means ref-
erence equality on reference types. While this is a bit more
ecient to implement, it also introduces a serious coherence
problem because boxed versions of equal values might no
longer be equal with respect to
==
.
Some situations require reference equality instead of
user-dened equality. An example is hash-consing, where
eciency is paramount. For these cases, class
AnyRef
de-
nes an additional
eq
method, which cannot be overridden,
and is implemented as reference equality (i.e., it behaves like
==
in Java for reference types).
3.2 Operations
Another aspect of Scala's unied object model is that ev-
ery operation is a message send, that is, the invocation of
a method. For instance the addition
x + y
is interpreted as
x.+(y)
, i.e. the invocation of the method
+
with
x
as the
receiver object and
y
as the method argument. This idea,
which has been applied originally in Smalltalk, is adapted
to the more conventional syntax of Scala as follows. First,
Scala treats operator names as ordinary identiers. More
precisely, an identier is either a sequence of letters and
digits starting with a letter, or a sequence of operator char-
acters. Hence, it is possible to dene methods called
+
,
<=
,
or
::
, for example. Next, Scala treats every occurrence of
an identier between two expressions as a method call. For
instance, in Listing 1, one could have used the operator syn-
tax
(arg startsWith "-")
as syntactic sugar for the more
conventional syntax
(arg.startsWith("-"))
.
As an example how user-dened operators are declared
and applied, consider the following implementation of a
class
Nat
for natural numbers. This class (very ine-
ciently) represents numbers as instances of two classes
Zero
and
Succ
. The number
N
would hence be represented as
new Succ
N
(Zero)
. We start the implementation with an ab-
stract class specifying the operations supported by natural
numbers. According to the denition of class
Nat
, natural
numbers provide two abstract methods
isZero
, and
pred
, as
well as three concrete methods
succ
,
+
, and
-
.
abstract class Nat {
def isZero: boolean
def pred: Nat
def succ: Nat = new Succ(this)
def + (x: Nat): Nat =
if (x.isZero) this else succ + x.pred
def - (x: Nat): Nat =
if (x.isZero) this else pred - x.pred
}
Note that Scala allows one to dene parameterless methods
such as
isZero
,
pred
, and
succ
in class
Nat
. Such methods
are invoked every time their name is selected; no argument
list is passed. Note also that abstract class members are
identied syntactically because they lack a denition; no
additional
abstract
modier is needed for them.
We now extend class
Nat
with a singleton object
Zero
and a class for representing successors,
Succ
.
object Zero extends Nat {
def isZero: boolean = true
def pred: Nat = throw new Error("Zero.pred")
override def toString: String = "Zero"
}
class Succ(n: Nat) extends Nat {
def isZero: boolean = false
def pred: Nat = n
override def toString: String = "Succ("+n+")"
}
The
Succ
class illustrates a dierence between the class def-
inition syntax of Scala and Java. In Scala, constructor pa-
rameters follow the class name; no separate class constructor
denition within the body of
Succ
is needed. This construc-
tor is called the
primary constructor
; the whole body of the
class is executed when the primary constructor is called at
the time the class is instantiated. There is syntax for
sec-
ondary constructors
in case more than one constructor is
desired (see Section 5.2.1 in [35]).
The
Zero
object and the
Succ
class both implement the
two abstract methods of their parent class,
Nat
. They also
override the
toString
method which they inherit from class
Any
. The
override
modier is required in Scala for methods
that override a concrete method in some inherited class; it is
optional for methods that implement some abstract method
in their superclass. The modier gives useful redundancy
to protect against two common class of errors. One class
of errors are accidental overrides, where a method in a sub-
class unintentionally overrides a method in a superclass. In
that case the Scala compiler would complain about a missing
override
modier. The other class of errors are broken over-
riding links. These arise when the parameters of a method
in a superclass are changed, but one forgets to change the
parameters of an overriding method in a subclass. Instead of
silently converting the override to an overloading, the Scala
compiler would in this case complain that the method in the
subclass overrides nothing.
The ability to have user-dened inx operators raises
the question about their relative precedence and associativ-
ity. One possibility would be to have xity-declarations in
the style of Haskell or SML, where users can declare these
properties of an operator individually. However, such decla-
rations tend to interact badly with modular programming.
Scala opts for a simpler scheme with xed precedences and
associativities. The precedence of an inx operator is deter-
mined by its rst character; it coincides with the operator
precedence of Java for those operators that start with an
operator character used in these languages. The following
lists operators in increasing precedence:
(all letters)
|
^
&
< >
= !
:
+ -
*
/ %
(all other special characters)
Operators are usually left-associative, i.e.
x + y + z
is
interpreted as
(x + y) + z
. The only exception to that
rule are operators
ending
in a colon. These are treated as
right-associative. An example is the list-consing operator
::
. Here,
x :: y :: zs
is interpreted as
x :: (y :: zs)
.
Right-associative operators are also treated dierently with
respect to method lookup. Whereas normal operators take
their left operand as receiver, right-associative operators
take their right operand as receiver. For instance, the list
4