Semantics of lazy vals in a class

I have an example of an attribute inhabited which is computationally difficult to compute.
Currently I have it defined as a def inhabited:Some[Boolean], and call sites have to be careful to not call it twice.
The def is defined in an abstract class and overridden in several subclasses. In each case the method does some computation. If that computation results in Some(true) or Some(false) then that option is returned. However if the computation results in None, then super.inhabited is returned.

Is there a way to have the system manage this laziness for me? As I understand a lazy val in a subclass, cannot through foo.super access the value of the val in the superclass. Right?

You could have a private lazy val and an accessor method in each class; the initialisation code for each private lazy val can then call the accessor method from the super class when needed?

Something like that:

trait MyTrait {
  def foo: Option[Boolean]
}

class MyParentClass extends MyTrait {
  private lazy val _foo: Option[Boolean] = Some(true)

  override def foo: Option[Boolean] = _foo
}

class MyChildClass extends MyParentClass {
  private lazy val _foo: Option[Boolean] = super.foo

  override def foo: Option[Boolean] = _foo
}

val x = new MyChildClass
println(x.foo) // Some(true)
1 Like

How about, in the base class, you say:

lazy val inhabited: Option[Boolean] = calculateInhabited()
protected def calculateInhabited(): Option[Boolean]

and then override calculateInhabited() in sub-classes

1 Like

Would that work for a chain of subclasses each inheriting from the next? I’m not really sure how protected interacts with inheritance.

the Clojure language has an interesting feature for this sort of thing, which is really easy to use. You can effectively mark a function (or function object) as memoizable. The system then (under the hood) maintains a hash table for the function from input parameters to output value. This does not effect the call-site, only the declaration site. You can make the memoization either global, so that it stays around forever, or make it local so it cleans up when the stack unwinds.

protected just means sub-classes can access that method, but other classes cannot.

1 Like

@curoli’s version is better than mine, it avoids having several lazy vals for a single instance of a subclass. :+1:

Let me see if I understand the claim. Does this mean that within the curly braces of a class definition of any subclass the method name, calculateInhabited is in scope, but given an object, obj, of the class obj.calculateInhabited() is not allowed?

@curoli, can you explain another thing that I’ve never really understood? I’ve define inhabited as a def with no parens.

override def inhabited: Option[Boolean] = { ... }

What does that mean? and how is it different from using empty parens?

override def inhabited(): Option[Boolean] = { ... }

I don’t recall the exact rules, but def m and def m() are distinct, but in Scala 2, if a def is declared with empty parens, you can also call it without parens. I believe there has been a proposal to change this in Scala 3, but I didn’t follow closely.

AFAIK the convention is to define def without parens if it acts like a (getter of a) field, i.e. stable return value, quick calculation, no noticeable side-effects. And with parens otherwise.