A block is a sequence of expressions grouped together as a single expression:
{a; b; c; d; e}
When executed, each expression in the block is executed in the order of appearance. (I.e. equential execution.)
Blocks are useful when several expressions need to be crammed into a space where a single expression is expected:
if (condition) {a; b; c} else {d; e; f}
while(condition) {a; b; c}
In Scala, the value of a block is the value of its last expression:
scala> {1; 2; 3} + {4; 5; 6}
<console>:8: warning: a pure expression does nothing in statement
position; you may be omitting necessary parentheses
{1; 2; 3} + {4; 5; 6}
^
<console>:8: warning: a pure expression does nothing in statement
position; you may be omitting necessary parentheses
{1; 2; 3} + {4; 5; 6}
^
res12: Int = 9
Some expressions in a block might be declarations. Recall that executing a declaration produces a binding between an identifier and a value. Also recall that a binding has scope and extent.
The scope of a binding created inside of a block is from the point of creation until the end of the block. A binding created inside of a block disappears when the block is exited.
It is possible for the scopes of several bindings for the same identifier to overlap. In this case the most recently created binding eclipses the others:
scala> {val x = 3; println(x); {val x = 9; println(x)};
println(x)}
3
9
3
In general a block can be collateral, recursive, or sequential. In a collateral block all expressions are executed in parallel, so a binding can be referenced by an expression that precedes the declaration that created the binding.
In a sequential block a binding may only be referenced after the declaration that creates it.
Scala uses recursive blocks, which allow a binding to be referenced by the declaration that creates it. This is useful for declaring recursive functions inside of a block:
scala> {print(x); val x = 2}
<console>:8: error: forward reference extends over definition of value x
{print(x); val x = 2}
^
scala> {def fact(n: Int):Int = if(n == 0) 1 else n * fact(n - 1);
print(fact(5))}
120
The Qualification Principle states:
The scope of a binding should be as small as possible.
Bindings can be altered inside of their scopes. If the scope is larger than it needs to be, then this opens the door for other parts of a program to misinterpret, alter, or misuse the binding.