1

In Scala, declaring a val as lazy means that its value won't be evaluated until it's used for the first time. This is often explained/demonstrated as being useful for optimization, in case a value might be expensive to compute but not needed at all.

It's also possible to use lazy in a way that it's necessary for the code to work correctly, rather than just for efficiency. For example, consider a lazy val like this:

lazy val foo = someObject.getList().find(pred) // don't use this until after someObject filled its list!

If foo weren't lazy, then it would always contain None, since its value would be evaluated immediately, before the list contained anything. Since it is lazy, it contains the right thing as long as it isn't evaluated before the list is filled.

My question: is it considered okay to use lazy in places like this where its absence would make the code incorrect, or should it be reserved for optimization only?

(Here is the real-world code snippet that inspired this question.)

  • 1
    It's not exactly without precedent. Many languages include short-circuit boolean operators that are often used as a fence to test one condition before evaluating another that depends on it. For example: if (someObject != null && someObject.someValue == 1); – Robert Harvey Jun 7 at 15:43
3

Yes, it is considered okay in general. A relatively common use is to initialize fields of a supertype when they have to depend on the state of a subtype (but even there you need to be careful). See https://docs.scala-lang.org/tutorials/FAQ/initialization-order.html.

In this particular example, "don't use this until after someObject filled its list" seems to put extra burden on clients of this class, so I'd look for alternatives.

0

Laziness is required for correctness in certain circumstances, like an infinite recursive data structure for example, but you haven't guaranteed correctness at all here. It's still possible to call foo before the list is populated. There are other better ways to write this that actually guarantee correctness. Those ways should be preferred if at all possible.

The first way is to make someObject immutable, so that its list must be fully populated before this object gets a reference to it. This is hard to do when you're accustomed to imperative code, but it's not as much of a burden as you might think. The payoff is you know the list is valid when you call foo. You don't have to guess whether foo is okay to call yet or not.

The next best way is to make someObject.getList() return a Future, or something similar like a monix Task or cats-effect IO. This is typically used when getList() is expensive to compute or might possibly fail, like if it requires a network call. A Future is how you specify in the type system that a value is still being computed, but it's not valid to use until the computation is finished.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.