1. Introduction
Scala provides the Predef object that is accessible directly in all the Scala classes without any imports. Some of the available methods from Predef are assert(), require(), println(), and so on. In this tutorial, we’ll look at yet another method, locally and how it can solve some problems.
2. Dangling Code Block Problem
In Java and Scala, we use {} to keep the code as part of the function and class bodies. Dangling code block refers to code blocks in curly brackets that are mistakenly interpreted differently than expected. This generally occurs due to the difference in positioning of the starting curly bracket.
Let’s look at an example where this dangling code block may arise. We can define a simple object:
object ABC {
val num = 100
}
ABC is an object that has a field num. We can rewrite this code block by having the opening { on a new line:
object ABC
{
val num = 100
}
However, if we accidentally place a new line between the object line and {, the meaning changes:
object ABC
{
val num = 100
}
In this case, the empty line causes the exclusion of the code block that defines the variable num from the object ABC. Instead, it’s treated as a separate code block with the sole responsibility of defining the variable num. Although this format isn’t recommended, it’s still considered legal. This specific situation is referred to as the Dangling Code Block Problem.
Moreover, this is legal based on the Scala syntax as it allows us to define any code block. For example, we can use a code block while defining a variable:
val num = {
val value = 100
println("Initialising variable `num` with " + value)
value
}
In the above example, we are using a code block to perform multiple operations while initializing the variable.
Additionally, Scala also lets us avoid using the {} while instantiating classes without any parameters:
class MyClass
lazy val myClass = new MyClass
{
println("This should be an independent block")
}
When running this code, the Scala compiler considers the code block containing the println() statement as part of MyClass. Consequently, the println() statement remains unexecuted until the lazy val is accessed for the first time.
If we want to separate it as an independent block, we need to explicitly add a new line in between:
class MyClass
lazy val myClass = new MyClass
{
println("*** This should be an independent block ***")
}
As a result of this new line, the Scala compiler considers the println() block separately, and upon executing this code block, it immediately prints the statement.
3. locally Block in Scala
To prevent the possibility of this dangling code block problem and control the boundary of code, Scala introduced the locally block. This enables us to explicitly indicate that the entire code block within the locally block is logically related and that we should consider it as a single unit.
Let’s re-write the last example with locally block:
class MyClass
lazy val myClass = new MyClass
locally {
println("This should be an independent block using locally")
}
Since we wrapped the code that contains the println() statement in a locally block, the compiler considers it as an independent code block.
Moreover, locally block doesn’t create any new scopes. But it helps to visually group related code blocks:
println("Performing some set of actions")
locally {
val a = 100
println("Inside locally block, performing something")
//do something
val b = 200
println(s"starting some background process using $a and $b")
}
val inst = "MyValue"
In the above sample, it doesn’t expose the variables a and b outside the locally block.
We can also add comments and documentation conveniently and clearly using the locally block:
def myMethod = {
val step1 = "some complicated step 1"
val step2 = locally {
// this step includes a lot of complicated logic
// doing this first
val a = 100
// doing second next complicated part
val b = 200
// some more complicated operation
a + b
}
}
In the above code, we can very clearly document and add comments to the complicated operation done in step2 by using the locally block.
4. Conclusion
In this article, we discussed the locally block in Scala and its usage. Functionally, the locally block is equivalent to a simple code block within {}. However, the locally block provides clarity, grouping, isolation, and readability. Additionally, it doesn’t introduce any new scopes and avoids any issues due to dangling code blocks.
As always, the sample code used in this article is available over on GitHub.