How to assign a Scala class field to a (lazy) block or function

This is an excerpt from the 1st Edition of the Scala Cookbook (partially modified for the internet). This is Recipe 4.8, “How to assign a Scala class field to a block or function.”

Problem

You want to initialize a field in a Scala class using a block of code, or by calling a function.

Solution

Set the field equal to the desired block of code or function. Optionally, define the field as lazy if the algorithm requires a long time to run.

In the following example, the field text is set equal to a block of code, which either returns (a) the text contained in a file, or (b) an error message, depending on whether the file exists and can be read:

class Foo {
    // set 'text' equal to the result of the block of code
    val text = {
        var lines = ""
        try {
          lines = io.Source.fromFile("/etc/passwd").getLines.mkString
        } catch {
          case e: Exception => lines = "Error happened"
        }
        lines
    }
    println(text)
}
object Test extends App {
    val f = new Foo
}

Because the assignment of the code block to the text field and the println statement are both in the body of the Foo class, they are in the class’s constructor, and will be executed when a new instance of the class is created. Therefore, compiling and running this example will either print the contents of the file, or the “Error happened” message from the catch block.

In a similar way, you can assign a class field to the results of a method or function:

class Foo {
    import scala.xml.XML

    // assign the xml field to the result of the load method
    val xml = XML.load("http://example.com/foo.xml")

    // more code here ...
}

Discussion

When it makes sense, define a field like this to be lazy, meaning it won’t be evaluated until it is accessed. To demonstrate this, ignore the potential for errors and shorten the class to this:

class Foo {
    val text = io.Source.fromFile("/etc/passwd").getLines.foreach(println)
}

object Test extends App {
    val f = new Foo
}

When this code is compiled and run on a Unix system, the contents of the /etc/passwd file are printed. That’s interesting, but notice what happens when you change the block to define the text field as lazy:

class Foo {
    lazy val text = io.Source.fromFile("/etc/passwd").getLines.foreach(println)
}
object Test extends App {
    val f = new Foo
}

When this code is compiled and run, there is no output, because the text field isn’t initialized until it’s accessed. That’s how a lazy field works.

Defining a field as lazy is a useful approach when the field might not be accessed in the normal processing of your algorithms, or if running the algorithm will take a long time, and you want to defer that to a later time.