title | layout | chapter |
---|---|---|
Expressions |
default |
6 |
Expr ::= (Bindings | id | ‘_’) ‘=>’ Expr
| Expr1
Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr]
| ‘while’ ‘(’ Expr ‘)’ {nl} Expr
| ‘try’ (‘{’ Block ‘}’ | Expr) [‘catch’ ‘{’ CaseClauses ‘}’] [‘finally’ Expr]
| ‘do’ Expr [semi] ‘while’ ‘(’ Expr ‘)’
| ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr
| ‘throw’ Expr
| ‘return’ [Expr]
| [SimpleExpr ‘.’] id ‘=’ Expr
| SimpleExpr1 ArgumentExprs ‘=’ Expr
| PostfixExpr
| PostfixExpr Ascription
| PostfixExpr ‘match’ ‘{’ CaseClauses ‘}’
PostfixExpr ::= InfixExpr [id [nl]]
InfixExpr ::= PrefixExpr
| InfixExpr id [nl] InfixExpr
PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr
SimpleExpr ::= ‘new’ (ClassTemplate | TemplateBody)
| BlockExpr
| SimpleExpr1 [‘_’]
SimpleExpr1 ::= Literal
| Path
| ‘_’
| ‘(’ [Exprs] ‘)’
| SimpleExpr ‘.’ id
| SimpleExpr TypeArgs
| SimpleExpr1 ArgumentExprs
| XmlExpr
Exprs ::= Expr {‘,’ Expr}
BlockExpr ::= ‘{’ CaseClauses ‘}’
| ‘{’ Block ‘}’
Block ::= BlockStat {semi BlockStat} [ResultExpr]
ResultExpr ::= Expr1
| (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block
Ascription ::= ‘:’ InfixType
| ‘:’ Annotation {Annotation}
| ‘:’ ‘_’ ‘*’
Expressions are composed of operators and operands. Expression forms are discussed subsequently in decreasing order of precedence.
The typing of expressions is often relative to some expected type (which might be undefined). When we write "expression
- the expected type of
$e$ is$T$ , and - the type of expression
$e$ must conform to$T$ .
The following skolemization rule is applied universally for every
expression: If the type of an expression would be an existential type
Skolemization is reversed by type packing. Assume an expression
$T$ forSome { type $t_1[\mathit{tps}\_1] >: L_1 <: U_1$; $\ldots$; type $t_n[\mathit{tps}\_n] >: L_n <: U_n$ }.
SimpleExpr ::= Literal
Typing of literals is described along with their lexical syntax; their evaluation is immediate.
The null
value is of type scala.Null
, and thus conforms to every reference type.
It denotes a reference value which refers to a special null
object.
This object implements methods in class scala.AnyRef
as follows:
-
eq($x\,$)
and==($x\,$)
returntrue
iff the argument$x$ is also the "null" object. -
ne($x\,$)
and!=($x\,$)
return true iff the argument x is not also the "null" object. -
isInstanceOf[$T\,$]
always returnsfalse
. -
asInstanceOf[$T\,$]
returns the default value of type$T$ . -
##
returns0
.
A reference to any other member of the "null" object causes a
NullPointerException
to be thrown.
SimpleExpr ::= Path
| SimpleExpr ‘.’ id
A designator refers to a named term. It can be a simple name or a selection.
A simple name $C$.this.$x$
where
If
For other expressions { val $y$ = $e$; $y$.$x$ }
, for some fresh name
The expected type of a designator's prefix is always undefined. The
type of a designator is the type $p$.type
.
The contexts where a stable type is required are those that satisfy one of the following conditions:
- The path
$p$ occurs as the prefix of a selection and it does not designate a constant, or - The expected type
$\mathit{pt}$ is a stable type, or - The expected type
$\mathit{pt}$ is an abstract type with a stable type as lower bound, and the type$T$ of the entity referred to by$p$ does not conform to$\mathit{pt}$ , or - The path
$p$ designates a module.
The selection
SimpleExpr ::= [id ‘.’] ‘this’
| [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id
The expression this
can appear in the statement part of a
template or compound type. It stands for the object being defined by
the innermost template or compound type enclosing the reference. If
this is a compound type, the type of this
is that compound type.
If it is a template of a
class or object definition with simple name $C$.this
.
The expression $C$.this
is legal in the statement part of an
enclosing class or object definition with simple name $C$.this
occurs as the prefix of a selection, its type is
$C$.this.type
, otherwise it is the self type of class
A reference super.$m$
refers statically to a method or type
If it is
a method, it must be concrete, or the template
containing the reference must have a member abstract override
.
A reference $C$.super.$m$
refers statically to a method
or type abstract override
.
The super
prefix may be followed by a trait qualifier
[$T\,$]
, as in $C$.super[$T\,$].$x$
. This is
called a static super reference. In this case, the reference is
to the type or method of
Consider the following class definitions
class Root { def x = "Root" }
class A extends Root { override def x = "A" ; def superA = super.x }
trait B extends Root { override def x = "B" ; def superB = super.x }
class C extends Root with B {
override def x = "C" ; def superC = super.x
}
class D extends A with B {
override def x = "D" ; def superD = super.x
}
The linearization of class C
is {C, B, Root}
and
the linearization of class D
is {D, B, A, Root}
.
Then we have:
(new A).superA == "Root"
(new C).superB == "Root"
(new C).superC == "B"
(new D).superA == "Root"
(new D).superB == "A"
(new D).superD == "B"
Note that the superB
method returns different results
depending on whether B
is mixed in with class Root
or A
.
SimpleExpr ::= SimpleExpr1 ArgumentExprs
ArgumentExprs ::= ‘(’ [Exprs] ‘)’
| ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’
| [nl] BlockExpr
Exprs ::= Expr {‘,’ Expr}
An application $f(e_1 , \ldots , e_m)$
applies the function $f$
to the argument expressions $e_1, \ldots , e_m$
. For this expression to be well-typed, the function must be applicable to its arguments, which is defined next by case analysis on
If ($p_1$:$T_1 , \ldots , p_n$:$T_n$)$U$
, each argument expression $x_i=e'_i$
and $x_i$
is one of the parameter names $p_1, \ldots, p_n$
.
Once the types
- for every named argument
$p_j=e_i'$ the type$S_i$ is compatible with the parameter type$T_j$ ; - for every positional argument
$e_i$ the type$S_i$ is compatible with$T_i$ ; - if the expected type is defined, the result type
$U$ is compatible to it.
If
If $f$.apply($e_1 , \ldots , e_m$)
,
i.e. the application of an apply
method defined by $f$
is applicable to the given arguments if $f$.apply
is applicable.
Evaluation of $f$($e_1 , \ldots , e_n$)
usually entails evaluation of
The case of a formal parameter with a parameterless
method type =>$T$
is treated specially. In this case, the
corresponding actual argument expression =>
-parameters is call-by-name whereas the evaluation
order for normal parameters is call-by-value.
Furthermore, it is required that val $y_i$ = () => $e$
and the argument passed to the function is $y_i$()
.
The last argument in an application may be marked as a sequence
argument, e.g. $e$: _*
. Such an argument must correspond
to a repeated parameter of type
$S$*
and it must be the only argument matching this
parameter (i.e. the number of formal parameters and actual arguments
must be the same). Furthermore, the type of scala.Seq[$T$]
, for some type
A function application usually allocates a new frame on the program's run-time stack. However, if a local method or a final method calls itself as its last action, the call is executed using the stack-frame of the caller.
Assume the following method which computes the sum of a variable number of arguments:
def sum(xs: Int*) = (0 /: xs) ((x, y) => x + y)
Then
sum(1, 2, 3, 4)
sum(List(1, 2, 3, 4): _*)
both yield 10
as result. On the other hand,
sum(List(1, 2, 3, 4))
would not typecheck.
If an application is to use named arguments
- For every named argument
$p_i = e_i$ which appears left of a positional argument in the argument list$e_1 \ldots e_m$ , the argument position$i$ coincides with the position of parameter$p_i$ in the parameter list of the applied method. - The names
$x_i$ of all named arguments are pairwise distinct and no named argument defines a parameter which is already specified by a positional argument. - Every formal parameter
$p_j:T_j$ which is not specified by either a positional or named argument has a default argument.
If the application uses named or default arguments the following transformation is applied to convert it into an application without named or default arguments.
If the method $p.m$[$\mathit{targs}$]
it is transformed into the
block
{ val q = $p$
q.$m$[$\mathit{targs}$]
}
If the method
{ val q = $p$
val $x_1$ = expr$_1$
$\ldots$
val $x_k$ = expr$_k$
q.$m$[$\mathit{targs}$]($\mathit{args}_1$)$, \ldots ,$($\mathit{args}_l$)
}
where every argument in $x_i=e'_i$
. Then, for every parameter which is not specified
by the argument list, a value definition using a fresh name
Let ($p_1:T_1 , \ldots , p_n:T_n$)$U$
.
The final result of the transformation is a block of the form
{ val q = $p$
val $x_1$ = expr$_1$
$\ldots$
val $x_l$ = expr$_k$
val $y_1$ = $e_1$
$\ldots$
val $y_m$ = $e_m$
val $z_1$ = $q.m\$default\$i[\mathit{targs}](\mathit{args}_1), \ldots ,(\mathit{args}_l)$
$\ldots$
val $z_d$ = $q.m\$default\$j[\mathit{targs}](\mathit{args}_1), \ldots ,(\mathit{args}_l)$
q.$m$[$\mathit{targs}$]($\mathit{args}_1$)$, \ldots ,$($\mathit{args}_l$)($\mathit{args}$)
}
For invocations of signature polymorphic methods of the target platform $f$($e_1 , \ldots , e_m$)
,
the invoked method has a different method type ($p_1$:$T_1 , \ldots , p_n$:$T_n$)$U$
at each call
site. The parameter types $T_ , \ldots , T_n$
are the types of the argument expressions
$e_1 , \ldots , e_m$
and $U$
is the expected type at the call site. If the expected type is
undefined then $U$
is scala.AnyRef
. The parameter names $p_1 , \ldots , p_n$
are fresh.
On the Java platform version 7 and later, the methods invoke
and invokeExact
in class
java.lang.invoke.MethodHandle
are signature polymorphic.
SimpleExpr ::= SimpleExpr1 ‘_’
The expression $e$ _
is well-formed if $e$ _
represents =>$T$
, $e$ _
represents the function of type
() => $T$
, which evaluates ()
.
The method values in the left column are each equivalent to the eta-expanded expressions on the right.
placeholder syntax | eta-expansion |
---|---|
math.sin _ |
x => math.sin(x) |
math.pow _ |
(x1, x2) => math.pow(x1, x2) |
val vs = 1 to 9; vs.fold _ |
(z) => (op) => vs.fold(z)(op) |
(1 to 9).fold(z)_ |
{ val eta1 = z; val eta2 = 1 to 9; op => eta2.fold(eta1)(op) } |
Some(1).fold(??? : Int)_ |
{ val eta1 = () => ???; val eta2 = Some(1); op => eta2.fold(eta1())(op) } |
Note that a space is necessary between a method name and the trailing underscore because otherwise the underscore would be considered part of the name.
SimpleExpr ::= SimpleExpr TypeArgs
A type application $e$[$T_1 , \ldots , T_n$]
instantiates
a polymorphic value [$a_1$ >: $L_1$ <: $U_1, \ldots , a_n$ >: $L_n$ <: $U_n$]$S$
with argument types
$T_1 , \ldots , T_n$
. Every argument type
If the function part $e$.apply[$T_1 , \ldots ,$ T$_n$]
, i.e. the application of an apply
method defined by
Type applications can be omitted if local type inference can infer best type parameters for a polymorphic method from the types of the actual method arguments and the expected result type.
SimpleExpr ::= ‘(’ [Exprs] ‘)’
A tuple expression ($e_1 , \ldots , e_n$)
is an alias
for the class instance creation
scala.Tuple$n$($e_1 , \ldots , e_n$)
, where ()
is the unique value of type scala.Unit
.
SimpleExpr ::= ‘new’ (ClassTemplate | TemplateBody)
A simple instance creation expression is of the form
new $c$
where scala.AnyRef
. Furthermore, the concrete self type of the
expression must conform to the self type of the class denoted by
new $c$
appears as the
right hand side of a value definition
val $x$: $S$ = new $c$
(where the type annotation : $S$
may be missing).
In the latter case, the concrete self type of the expression is the
compound type $T$ with $x$.type
.
The expression is evaluated by creating a fresh
object of type
A general instance creation expression is of the form
new $t$
for some class template
{ class $a$ extends $t$; new $a$ }
where
There is also a shorthand form for creating values of structural
types: If {$D$}
is a class body, then
new {$D$}
is equivalent to the general instance creation expression
new AnyRef{$D$}
.
Consider the following structural instance creation expression:
new { def getName() = "aaron" }
This is a shorthand for the general instance creation expression
new AnyRef{ def getName() = "aaron" }
The latter is in turn a shorthand for the block
{ class anon\$X extends AnyRef{ def getName() = "aaron" }; new anon\$X }
where anon\$X
is some freshly created name.
BlockExpr ::= ‘{’ CaseClauses ‘}’
| ‘{’ Block ‘}’
Block ::= BlockStat {semi BlockStat} [ResultExpr]
A block expression {$s_1$; $\ldots$; $s_n$; $e\,$}
is
constructed from a sequence of block statements ()
is assumed.
The expected type of the final expression
The type of a block $s_1$; $\ldots$; $s_n$; $e$
is
$T$ forSome {$\,Q\,$}
, where
- A locally defined type definition
type$\;t = T$
is bound by the existential clausetype$\;t >: T <: T$
. It is an error if$t$ carries type parameters. - A locally defined value definition
val$\;x: T = e$
is bound by the existential clauseval$\;x: T$
. - A locally defined class definition
class$\;c$ extends$\;t$
is bound by the existential clausetype$\;c <: T$
where$T$ is the least class type or refinement type which is a proper supertype of the type$c$ . It is an error if$c$ carries type parameters. - A locally defined object definition
object$\;x\;$extends$\;t$
is bound by the existential clauseval$\;x: T$
where$T$ is the least class type or refinement type which is a proper supertype of the type$x$.type
.
Evaluation of the block entails evaluation of its
statement sequence, followed by an evaluation of the final expression
Assuming a class Ref[T](x: T)
, the block
{ class C extends B {$\ldots$} ; new Ref(new C) }
has the type Ref[_1] forSome { type _1 <: B }
.
The block
{ class C extends B {$\ldots$} ; new C }
simply has type B
, because with the rules here
the existentially quantified type
_1 forSome { type _1 <: B }
can be simplified to B
.
PostfixExpr ::= InfixExpr [id [nl]]
InfixExpr ::= PrefixExpr
| InfixExpr id [nl] InfixExpr
PrefixExpr ::= [‘-’ | ‘+’ | ‘!’ | ‘~’] SimpleExpr
Expressions can be constructed from operands and operators.
A prefix operation +
’, ‘-
’,
‘!
’ or ‘~
’. The expression e.unary_$\mathit{op}$
.
Prefix operators are different from normal method applications in
that their operand expression need not be atomic. For instance, the
input sequence -sin(x)
is read as -(sin(x))
, whereas the
method application negate sin(x)
would be parsed as the
application of the infix operator sin
to the operands
negate
and (x)
.
A postfix operator can be an arbitrary identifier. The postfix
operation
An infix operator can be an arbitrary identifier. Infix operators have precedence and associativity defined as follows:
The precedence of an infix operator is determined by the operator's first character. Characters are listed below in increasing order of precedence, with characters on the same line having the same precedence.
(all letters)
|
^
&
= !
< >
:
+ -
* / %
(all other special characters)
That is, operators starting with a letter have lowest precedence,
followed by operators starting with ‘|
’, etc.
There's one exception to this rule, which concerns
assignment operators.
The precedence of an assignment operator is the same as the one
of simple assignment (=)
. That is, it is lower than the
precedence of any other operator.
The associativity of an operator is determined by the operator's
last character. Operators ending in a colon ‘:
’ are
right-associative. All other operators are left-associative.
Precedence and associativity of operators determine the grouping of parts of an expression as follows.
- If there are several infix operations in an expression, then operators with higher precedence bind more closely than operators with lower precedence.
- If there are consecutive infix
operations
$e_0; \mathit{op}_1; e_1; \mathit{op}_2 \ldots \mathit{op}_n; e_n$ with operators$\mathit{op}_1 , \ldots , \mathit{op}_n$ of the same precedence, then all these operators must have the same associativity. If all operators are left-associative, the sequence is interpreted as$(\ldots(e_0;\mathit{op}_1;e_1);\mathit{op}_2\ldots);\mathit{op}_n;e_n$ . Otherwise, if all operators are right-associative, the sequence is interpreted as$e_0;\mathit{op}_1;(e_1;\mathit{op}_2;(\ldots \mathit{op}_n;e_n)\ldots)$ . - Postfix operators always have lower precedence than infix
operators. E.g.
$e_1;\mathit{op}_1;e_2;\mathit{op}_2$ is always equivalent to$(e_1;\mathit{op}_1;e_2);\mathit{op}_2$ .
The right-hand operand of a left-associative operator may consist of
several arguments enclosed in parentheses, e.g.
A left-associative binary
operation { val $x$=$e_1$; $e_2$.$\mathit{op}$($x\,$) }
, where
An assignment operator is an operator symbol (syntax category
op
in Identifiers) that ends in an equals character
“=
”, with the exception of operators for which one of
the following conditions holds:
- the operator also starts with an equals character, or
- the operator is one of
(<=)
,(>=)
,(!=)
.
Assignment operators are treated specially in that they can be expanded to assignments if no other interpretation is valid.
Let's consider an assignment operator such as +=
in an infix
operation $l$ += $r$
, where
$l$ = $l$ + $r$
except that the operation's left-hand-side
The re-interpretation occurs if the following two conditions are fulfilled.
- The left-hand-side
$l$ does not have a member named+=
, and also cannot be converted by an implicit conversion to a value with a member named+=
. - The assignment
$l$ = $l$ + $r$
is type-correct. In particular this implies that$l$ refers to a variable or object that can be assigned to, and that is convertible to a value with a member named+
.
Expr1 ::= PostfixExpr ‘:’ CompoundType
The typed expression
Here are examples of well-typed and ill-typed expressions.
1: Int // legal, of type Int
1: Long // legal, of type Long
// 1: string // ***** illegal
Expr1 ::= PostfixExpr ‘:’ Annotation {Annotation}
An annotated expression $e$: @$a_1$ $\ldots$ @$a_n$
attaches annotations
Expr1 ::= [SimpleExpr ‘.’] id ‘=’ Expr
| SimpleExpr1 ArgumentExprs ‘=’ Expr
The interpretation of an assignment to a simple variable $x$ = $e$
depends on the definition of $x$_=
as member, then the assignment
$x$ = $e$
is interpreted as the invocation
$x$_=($e\,$)
of that setter method. Analogously, an
assignment $f.x$ = $e$
to a parameterless method $f.x$_=($e\,$)
.
An assignment $f$($\mathit{args}\,$) = $e$
with a method application to the
left of the ‘=
’ operator is interpreted as
$f.$update($\mathit{args}$, $e\,$)
, i.e.
the invocation of an update
method defined by
Here are some assignment expressions and their equivalent expansions.
assignment | expansion |
---|---|
x.f = e |
x.f_=(e) |
x.f() = e |
x.f.update(e) |
x.f(i) = e |
x.f.update(i, e) |
x.f(i, j) = e |
x.f.update(i, j, e) |
Here is the usual imperative code for matrix multiplication.
def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = {
val zss: Array[Array[Double]] = new Array(xss.length, yss(0).length)
var i = 0
while (i < xss.length) {
var j = 0
while (j < yss(0).length) {
var acc = 0.0
var k = 0
while (k < yss.length) {
acc = acc + xss(i)(k) * yss(k)(j)
k += 1
}
zss(i)(j) = acc
j += 1
}
i += 1
}
zss
}
Desugaring the array accesses and assignments yields the following expanded version:
def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = {
val zss: Array[Array[Double]] = new Array(xss.length, yss.apply(0).length)
var i = 0
while (i < xss.length) {
var j = 0
while (j < yss.apply(0).length) {
var acc = 0.0
var k = 0
while (k < yss.length) {
acc = acc + xss.apply(i).apply(k) * yss.apply(k).apply(j)
k += 1
}
zss.apply(i).update(j, acc)
j += 1
}
i += 1
}
zss
}
Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr]
The conditional expression if ($e_1$) $e_2$ else $e_3$
chooses
one of the values of Boolean
. The then-part else
symbol of a
conditional expression is ignored.
The conditional expression is evaluated by evaluating first
true
, the result of
evaluating
A short form of the conditional expression eliminates the
else-part. The conditional expression if ($e_1$) $e_2$
is
evaluated as if it was if ($e_1$) $e_2$ else ()
.
Expr1 ::= ‘while’ ‘(’ Expr ‘)’ {nl} Expr
The while loop expression while ($e_1$) $e_2$
is typed and
evaluated as if it was an application of whileLoop ($e_1$) ($e_2$)
where
the hypothetical method whileLoop
is defined as follows.
def whileLoop(cond: => Boolean)(body: => Unit): Unit =
if (cond) { body ; whileLoop(cond)(body) } else {}
Expr1 ::= ‘do’ Expr [semi] ‘while’ ‘(’ Expr ‘)’
The do loop expression do $e_1$ while ($e_2$)
is typed and
evaluated as if it was the expression ($e_1$ ; while ($e_2$) $e_1$)
.
A semicolon preceding the while
symbol of a do loop expression is ignored.
Expr1 ::= ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’)
{nl} [‘yield’] Expr
Enumerators ::= Generator {semi Generator}
Generator ::= Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr}
Guard ::= ‘if’ PostfixExpr
A for loop for ($\mathit{enums}\,$) $e$
executes expression for ($\mathit{enums}\,$) yield $e$
evaluates
expression $p$ <- $e$
produces bindings from an expression $p$ = $e$
binds the value name if $e$
contains a boolean expression which restricts
enumerated bindings. The precise meaning of generators and guards is
defined by translation to invocations of four methods: map
,
withFilter
, flatMap
, and foreach
. These methods can
be implemented in different ways for different carrier types.
The translation scheme is as follows. In a first step, every
generator $p$ <- $e$
, where
$p$ <- $e$.withFilter { case $p$ => true; case _ => false }
Then, the following rules are applied repeatedly until all comprehensions have been eliminated.
-
A for comprehension
for ($p$ <- $e\,$) yield $e'$
is translated to$e$.map { case $p$ => $e'$ }
. -
A for loop
for ($p$ <- $e\,$) $e'$
is translated to$e$.foreach { case $p$ => $e'$ }
. -
A for comprehension
for ($p$ <- $e$; $p'$ <- $e'; \ldots$) yield $e''$
where
$\ldots$
is a (possibly empty) sequence of generators, definitions, or guards, is translated to$e$.flatMap { case $p$ => for ($p'$ <- $e'; \ldots$) yield $e''$ }
-
A for loop
for ($p$ <- $e$; $p'$ <- $e'; \ldots$) $e''$
where
$\ldots$
is a (possibly empty) sequence of generators, definitions, or guards, is translated to$e$.foreach { case $p$ => for ($p'$ <- $e'; \ldots$) $e''$ }
-
A generator
$p$ <- $e$
followed by a guardif $g$
is translated to a single generator$p$ <- $e$.withFilter(($x_1 , \ldots , x_n$) => $g\,$)
where$x_1 , \ldots , x_n$ are the free variables of$p$ . -
A generator
$p$ <- $e$
followed by a value definition$p'$ = $e'$
is translated to the following generator of pairs of values, where$x$ and$x'$ are fresh names:($p$, $p'$) <- for ($x @ p$ <- $e$) yield { val $x' @ p'$ = $e'$; ($x$, $x'$) }
The following code produces all pairs of numbers between
for { i <- 1 until n
j <- 1 until i
if isPrime(i+j)
} yield (i, j)
The for comprehension is translated to:
(1 until n)
.flatMap {
case i => (1 until i)
.withFilter { j => isPrime(i+j) }
.map { case j => (i, j) } }
For comprehensions can be used to express vector and matrix algorithms concisely. For instance, here is a method to compute the transpose of a given matrix:
def transpose[A](xss: Array[Array[A]]) = {
for (i <- Array.range(0, xss(0).length)) yield
for (xs <- xss) yield xs(i)
}
Here is a method to compute the scalar product of two vectors:
def scalprod(xs: Array[Double], ys: Array[Double]) = {
var acc = 0.0
for ((x, y) <- xs zip ys) acc = acc + x * y
acc
}
Finally, here is a method to compute the product of two matrices. Compare with the imperative version.
def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = {
val ysst = transpose(yss)
for (xs <- xss) yield
for (yst <- ysst) yield
scalprod(xs, yst)
}
The code above makes use of the fact that map
, flatMap
,
withFilter
, and foreach
are defined for instances of class
scala.Array
.
Expr1 ::= ‘return’ [Expr]
A return expression return $e$
must occur inside the body of some
enclosing user defined method. The innermost enclosing method in a
source program,
The return expression evaluates the expression scala.Nothing
.
The expression return
is type-checked and evaluated as if it were return ()
.
Returning from the method from within a nested function may be
implemented by throwing and catching a
scala.runtime.NonLocalReturnException
. Any exception catches
between the point of return and the enclosing methods might see
and catch that exception. A key comparison makes sure that this
exception is only caught by the method instance which is terminated
by the return.
If the return expression is itself part of an anonymous function, it
is possible that the enclosing method scala.runtime.NonLocalReturnException
will not be caught, and will
propagate up the call stack.
Expr1 ::= ‘throw’ Expr
A throw expression throw $e$
evaluates the expression
Throwable
. If null
, evaluation is instead aborted with a
NullPointerException
. If there is an active
try
expression which handles the thrown
exception, evaluation resumes with the handler; otherwise the thread
executing the throw
is aborted. The type of a throw expression
is scala.Nothing
.
Expr1 ::= ‘try’ (‘{’ Block ‘}’ | Expr) [‘catch’ ‘{’ CaseClauses ‘}’]
[‘finally’ Expr]
A try expression is of the form try { $b$ } catch $h$
where the handler
{ case $p_1$ => $b_1$ $\ldots$ case $p_n$ => $b_n$ }
This expression is evaluated by evaluating the block
Let scala.PartialFunction[scala.Throwable, $\mathit{pt}\,$]
.
The type of the try expression is the weak least upper bound
of the type of
A try expression try { $b$ } finally $e$
evaluates the block
If an exception is thrown during evaluation of Unit
.
A try expression try { $b$ } catch $e_1$ finally $e_2$
is a shorthand
for try { try { $b$ } catch $e_1$ } finally $e_2$
.
Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr
ResultExpr ::= (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block
Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’
Binding ::= (id | ‘_’) [‘:’ Type]
The anonymous function of arity ($x_1$: $T_1 , \ldots , x_n$: $T_n$) => e
maps parameters
In the case of a single untyped formal parameter, ($x\,$) => $e$
can be abbreviated to $x$ => $e$
. If an anonymous function ($x$: $T\,$) => $e$
with a single typed parameter appears as the result expression of a block, it can be abbreviated to $x$: $T$ => e
.
A formal parameter may also be a wildcard represented by an underscore _
. In that case, a fresh name for the parameter is chosen arbitrarily.
A named parameter of an anonymous function may be optionally preceded by an implicit
modifier. In that case the parameter is labeled implicit
; however the parameter section itself does not count as an implicit parameter section. Hence, arguments to anonymous functions always have to be given explicitly.
If the expected type of the anonymous function is of the shape scala.Function$n$[$S_1 , \ldots , S_n$, $R\,$]
, or can be SAM-converted to such a function type, the type $T_i$
of a parameter $x_i$
can be omitted, as far as $S_i$
is defined in the expected type, and $T_i$ = $S_i$
is assumed. Furthermore, the expected type when type checking
If there is no expected type for the function literal, all formal parameter types $T_i$
must be specified explicitly, and the expected type of scala.Function$n$[$T_1 , \ldots , T_n$, $R\,$]
, where
The eventual run-time value of an anonymous function is determined by the expected type:
- a subclass of one of the builtin function types,
scala.Function$n$[$S_1 , \ldots , S_n$, $R\,$]
(with$S_i$ and$R$ fully defined), - a single-abstract-method (SAM) type;
-
PartialFunction[$T$, $U$]
, if the function literal is of the shapex => x match { $\ldots$ }
- some other type.
The standard anonymous function evaluates in the same way as the following instance creation expression:
new scala.Function$n$[$T_1 , \ldots , T_n$, $T$] {
def apply($x_1$: $T_1 , \ldots , x_n$: $T_n$): $T$ = $e$
}
The same evaluation holds for a SAM type, except that the instantiated type is given by the SAM type, and the implemented method is the single abstract method member of this type.
The underlying platform may provide more efficient ways of constructing these instances, such as Java 8's invokedynamic
bytecode and LambdaMetaFactory
class.
A PartialFunction
's value receives an additional isDefinedAt
member, which is derived from the pattern match in the function literal, with each case's body being replaced by true
, and an added default (if none was given) that evaluates to false
.
Examples of anonymous functions:
x => x // The identity function
f => g => x => f(g(x)) // Curried function composition
(x: Int,y: Int) => x + y // A summation function
() => { count += 1; count } // The function which takes an
// empty parameter list $()$,
// increments a non-local variable
// `count' and returns the new value.
_ => 5 // The function that ignores its argument
// and always returns 5.
SimpleExpr1 ::= ‘_’
An expression (of syntactic category Expr
)
may contain embedded underscore symbols _
at places where identifiers
are legal. Such an expression represents an anonymous function where subsequent
occurrences of underscores denote successive parameters.
Define an underscore section to be an expression of the form
_:$T$
where _
,
provided the underscore does not appear as the expression part of a
type ascription _:$T$
.
An expression Expr
binds an underscore section
Expr
which is properly contained in
If an expression ($u'_1$, ... $u'_n$) => $e'$
where each
The anonymous functions in the left column use placeholder syntax. Each of these is equivalent to the anonymous function on its right.
_ + 1 |
x => x + 1 |
_ * _ |
(x1, x2) => x1 * x2 |
(_: Int) * 2 |
(x: Int) => (x: Int) * 2 |
if (_) x else y |
z => if (z) x else y |
_.map(f) |
x => x.map(f) |
_.map(_ + 1) |
x => x.map(y => y + 1) |
Constant expressions are expressions that the Scala compiler can evaluate to a constant. The definition of "constant expression" depends on the platform, but they include at least the expressions of the following forms:
- A literal of a value class, such as an integer
- A string literal
- A class constructed with
Predef.classOf
- An element of an enumeration from the underlying platform
- A literal array, of the form
Array$(c_1 , \ldots , c_n)$
, where all of the$c_i$ 's are themselves constant expressions - An identifier defined by a constant value definition.
BlockStat ::= Import
| {Annotation} [‘implicit’ | ‘lazy’] Def
| {Annotation} {LocalModifier} TmplDef
| Expr1
|
TemplateStat ::= Import
| {Annotation} {Modifier} Def
| {Annotation} {Modifier} Dcl
| Expr
|
Statements occur as parts of blocks and templates. A statement can be
an import, a definition or an expression, or it can be empty.
Statements used in the template of a class definition can also be
declarations. An expression that is used as a statement can have an
arbitrary value type. An expression statement
Block statements may be definitions which bind local names in the
block. The only modifier allowed in all block-local definitions is
implicit
. When prefixing a class or object definition,
modifiers abstract
, final
, and sealed
are also
permitted.
Evaluation of a statement sequence entails evaluation of the statements in the order they are written.
Implicit conversions can be applied to expressions whose type does not match their expected type, to qualifiers in selections, and to unapplied methods. The available implicit conversions are given in the next two sub-sections.
The following seven implicit conversions can be applied to an
expression
If an expression denotes several possible members of a class, overloading resolution is applied to pick a unique member.
An expression
[$a_1$ >: $L_1$ <: $U_1 , \ldots , a_n$ >: $L_n$ <: $U_n$]$T$
which does not appear as the function part of
a type application is converted to a type instance of $T_1 , \ldots , T_n$
for the type variables $a_1 , \ldots , a_n$
and
implicitly embedding $e$[$T_1 , \ldots , T_n$]
.
If toShort
, toChar
, toInt
, toLong
,
toFloat
, toDouble
defined here.
If the expected type is Byte
, Short
or Char
, and
the expression
If Unit
,
{ $e$; () }
.
An expression (p1, ..., pN) => body
of function type (T1, ..., TN) => T
is sam-convertible to the expected type S
if the following holds:
- the class
C
ofS
declares an abstract methodm
with signature(p1: A1, ..., pN: AN): R
; - besides
m
,C
must not declare or inherit any other deferred value members; - the method
m
must have a single argument list; - there must be a type
U
that is a subtype ofS
, so that the expressionnew U { final def m(p1: A1, ..., pN: AN): R = body }
is well-typed (conforming to the expected typeS
); - for the purpose of scoping,
m
should be considered a static member (U
's members are not in scope inbody
); (A1, ..., AN) => R
is a subtype of(T1, ..., TN) => T
(satisfying this condition drives type inference of unknown type parameters inS
);
Note that a function literal that targets a SAM is not necessarily compiled to the above instance creation expression. This is platform-dependent.
It follows that:
- if class
C
defines a constructor, it must be accessible and must define exactly one, empty, argument list; - class
C
cannot befinal
orsealed
(for simplicity we ignore the possibility of SAM conversion in the same compilation unit as the sealed class); m
cannot be polymorphic;- it must be possible to derive a fully-defined type
U
fromS
by inferring any unknown type parameters ofC
.
Finally, we impose some implementation restrictions (these may be lifted in future releases):
C
must not be nested or local (it must not capture its environment, as that results in a nonzero-argument constructor)C
's constructor must not have an implicit argument list (this simplifies type inference);C
must not declare a self type (this simplifies type inference);C
must not be@specialized
.
If none of the previous conversions applies, and
If none of the previous conversions applies, and scala.Dynamic
,
then the selection is rewritten according to the rules for
dynamic member selection.
The following four implicit conversions can be applied to methods which are not applied to some argument list.
A parameterless method => $T$
is always converted to
type
If the method takes only implicit parameters, implicit arguments are passed following the rules here.
Otherwise, if the method is not a constructor,
and the expected type
Otherwise, if
If an identifier or selection
Assume first that $e$($e_1 , \ldots , e_m$)
.
One first determines the set of functions that is potentially applicable based on the shape of the arguments.
The shape of an argument expression
- For a function expression
($p_1$: $T_1 , \ldots , p_n$: $T_n$) => $b$: (Any $, \ldots ,$ Any) => $\mathit{shape}(b)$
, whereAny
occurs$n$ times in the argument type. - For a pattern-matching anonymous function definition
{ case ... }
:PartialFunction[Any, Nothing]
. - For a named argument
$n$ = $e$
:$\mathit{shape}(e)$ . - For all other expressions:
Nothing
.
Let
Otherwise, let
Normally, an argument is typed without an expected type, except when trying to propagate more type
information to aid inference of higher-order function parameter types, as explained next. The intuition is
that all arguments must be of a function-like type (PartialFunction
, FunctionN
or some equivalent SAM type),
which in turn must define the same set of higher-order argument types, so that they can safely be used as
the expected type of a given argument of the overloaded method, without unduly ruling out any alternatives.
The intent is not to steer overloading resolution, but to preserve enough type information to steer type
inference of the arguments (a function literal or eta-expanded method) to this overloaded method.
Note that the expected type drives eta-expansion (not performed unless a function-like type is expected), as well as inference of omitted parameter types of function literals.
More precisely, an argument $e_i$
is typed with an expected type that is derived from the $i$
th argument
type found in each alternative (call these $T_{ij}$
for alternative $j$
and argument position $i$
) when
all $T_{ij}$
are function types $(A_{1j},..., A_{nj}) => ?$
(or the equivalent PartialFunction
, or SAM)
of some arity $n$
, and their argument types $A_{kj}$
are identical across all overloads $j$
for a
given $k$
. Then, the expected type for $e_i$
is derived as follows:
- we use
$PartialFunction[A_{1j},..., A_{nj}, ?]$
if for some overload$j$
,$T_{ij}$
's type symbol isPartialFunction
; - else, if for some
$j$
,$T_{ij}$
isFunctionN
, the expected type is$FunctionN[A_{1j},..., A_{nj}, ?]$
; - else, if for all
$j$
,$T_{ij}$
is a SAM type of the same class, defining argument types$A_{1j},..., A_{nj}$
(and a potentially varying result type), the expected type encodes these argument types and the SAM class.
For every member
It is an error if none of the members in
It is again an error if
- A parameterized method
$m$ of type($p_1:T_1, \ldots , p_n:T_n$)$U$
is as specific as some other member$m'$ of type$S$ if$m'$ is applicable to arguments($p_1 , \ldots , p_n$)
of types$T_1 , \ldots , T_n$ . - A polymorphic method of type
[$a_1$ >: $L_1$ <: $U_1 , \ldots , a_n$ >: $L_n$ <: $U_n$]$T$
is as specific as some other member of type$S$ if$T$ is as specific as$S$ under the assumption that for$i = 1 , \ldots , n$ each$a_i$ is an abstract type name bounded from below by$L_i$ and from above by$U_i$ . - A member of any other type is always as specific as a parameterized method or a polymorphic method.
- Given two members of types
$T$ and$U$ which are neither parameterized nor polymorphic method types, the member of type$T$ is as specific as the member of type$U$ if the existential dual of$T$ conforms to the existential dual of$U$ . Here, the existential dual of a polymorphic type[$a_1$ >: $L_1$ <: $U_1 , \ldots , a_n$ >: $L_n$ <: $U_n$]$T$
is$T$ forSome { type $a_1$ >: $L_1$ <: $U_1$ $, \ldots ,$ type $a_n$ >: $L_n$ <: $U_n$}
. The existential dual of every other type is the type itself.
The relative weight of an alternative
- 1 if
$A$ is as specific as$B$ , 0 otherwise, and - 1 if
$A$ is defined in a class or object which is derived from the class or object defining$B$ , 0 otherwise.
A class or object
-
$C$ is a subclass of$D$ , or -
$C$ is a companion object of a class derived from$D$ , or -
$D$ is a companion object of a class from which$C$ is derived.
An alternative
It is an error if there is no alternative in
Assume next that $e$[$\mathit{targs}\,$]
. Then all alternatives in
$e$[$\mathit{targs}\,$]
.
Assume finally that
Consider the following definitions:
class A extends B {}
def f(x: B, y: B) = $\ldots$
def f(x: A, y: B) = $\ldots$
val a: A
val b: B
Then the application f(b, b)
refers to the first
definition of f(a, a)
refers to the second. Assume now we add a third overloaded definition
def f(x: B, y: A) = $\ldots$
Then the application f(a, a)
is rejected for being ambiguous, since
no most specific applicable signature exists.
Local type inference infers type arguments to be passed to expressions
of polymorphic type. Say
Local type inference converts this expression to a type
application $e$[$T_1 , \ldots , T_n$]
. The choice of the
type arguments
If the expression appears as the prefix of a selection with a name
If the expression
- None of the inferred types
$T_i$ is a singleton type unless it is a singleton type corresponding to an object or a constant value definition or the corresponding bound$U_i$ is a subtype ofscala.Singleton
. - All type parameter bounds are respected, i.e.
$\sigma L_i <: \sigma a_i$ and$\sigma a_i <: \sigma U_i$ for$i = 1 , \ldots , n$ . - The expression's type conforms to the expected type, i.e.
$\sigma T <: \sigma \mathit{pt}$ .
It is a compile time error if no such substitution exists.
If several substitutions exist, local-type inference will choose for
each type variable
The last case applies if the expression
In a second step, type arguments are inferred by solving a constraint
system which relates the method's type with the expected type
- None of the inferred types
$T_i$ is a singleton type unless it is a singleton type corresponding to an object or a constant value definition or the corresponding bound$U_i$ is a subtype ofscala.Singleton
. - All type parameter bounds are respected, i.e.
$\sigma L_i <: \sigma a_i$ and$\sigma a_i <: \sigma U_i$ for$i = 1 , \ldots , n$ . - The method's result type
$T'$ conforms to the expected type, i.e.$\sigma T' <: \sigma \mathit{pt}$ . - Each argument type weakly conforms
to the corresponding formal parameter
type, i.e.
$\sigma S_j <:_w \sigma R_j$ for$j = 1 , \ldots , m$ .
It is a compile time error if no such substitution exists. If several
solutions exist, an optimal one for the type
All or parts of an expected type
It is possible that no minimal or maximal solution for a type variable
exists, in which case a compile-time error results. Because
Consider the two methods:
def cons[A](x: A, xs: List[A]): List[A] = x :: xs
def nil[B]: List[B] = Nil
and the definition
val xs = cons(1, nil)
The application of cons
is typed with an undefined expected
type. This application is completed by local type inference to
cons[Int](1, nil)
.
Here, one uses the following
reasoning to infer the type argument Int
for the type
parameter a
:
First, the argument expressions are typed. The first argument 1
has type Int
whereas the second argument nil
is
itself polymorphic. One tries to type-check nil
with an
expected type List[a]
. This leads to the constraint system
List[b?] <: List[a]
where we have labeled b?
with a question mark to indicate
that it is a variable in the constraint system.
Because class List
is covariant, the optimal
solution of this constraint is
b = scala.Nothing
In a second step, one solves the following constraint system for
the type parameter a
of cons
:
Int <: a?
List[scala.Nothing] <: List[a?]
List[a?] <: $\mathit{undefined}$
The optimal solution of this constraint system is
a = Int
so Int
is the type inferred for a
.
Consider now the definition
val ys = cons("abc", xs)
where xs
is defined of type List[Int]
as before.
In this case local type inference proceeds as follows.
First, the argument expressions are typed. The first argument
"abc"
has type String
. The second argument xs
is
first tried to be typed with expected type List[a]
. This fails,
as List[Int]
is not a subtype of List[a]
. Therefore,
the second strategy is tried; xs
is now typed with expected type
List[$\mathit{undefined}$]
. This succeeds and yields the argument type
List[Int]
.
In a second step, one solves the following constraint system for
the type parameter a
of cons
:
String <: a?
List[Int] <: List[a?]
List[a?] <: $\mathit{undefined}$
The optimal solution of this constraint system is
a = scala.Any
so scala.Any
is the type inferred for a
.
Eta-expansion converts an expression of method type to an equivalent expression of function type. It proceeds in two steps.
First, one identifies the maximal sub-expressions of
{ val $x_1$ = $e_1$;
$\ldots$
val $x_m$ = $e_m$;
($y_1: T_1 , \ldots , y_n: T_n$) => $e'$($y_1 , \ldots , y_n$)
}
The behavior of call-by-name parameters is preserved under eta-expansion: the corresponding actual argument expression, a sub-expression of parameterless method type, is not evaluated in the expanded block.
The standard Scala library defines a marker trait scala.Dynamic
. Subclasses of this trait are able to intercept selections and applications on their instances by defining methods of the names applyDynamic
, applyDynamicNamed
, selectDynamic
, and updateDynamic
.
The following rewrites are performed, assuming scala.Dynamic
, and the original expression does not type check under the normal rules, as specified fully in the relevant subsection of implicit conversion:
e.m[Ti](xi)
becomese.applyDynamic[Ti]("m")(xi)
e.m[Ti]
becomese.selectDynamic[Ti]("m")
e.m = x
becomese.updateDynamic("m")(x)
If any arguments are named in the application (one of the xi
is of the shape arg = x
), their name is preserved as the first component of the pair passed to applyDynamicNamed
(for missing names, ""
is used):
e.m[Ti](argi = xi)
becomese.applyDynamicNamed[Ti]("m")(("argi", xi))
Finally:
e.m(x) = y
becomese.selectDynamic("m").update(x, y)
None of these methods are actually defined in the scala.Dynamic
, so that users are free to define them with or without type parameters, or implicit arguments.