Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy Val - How to reset value?

I may want to use a method that is expensive and returns a result depending on side effects. For example depending on the time of day / week and a Monte Carlo simulation of quantum chronodynamics. Because it's expensive and I may not need it, I'll use Scalas lazy val

lazy val maybeUnusedValue = getDayOfWeek

Now 12 hours pass with my program still running. I want to redo the calculation, because my day may have changed in the meantime.

Is there a simple way to force Scala to return maybeUnusedValue back to uninitialized state, therefore forcing a recalculation on its next usage?

like image 909
5-to-9 Avatar asked Dec 25 '22 19:12

5-to-9


2 Answers

Is there a simple way

Since "laziness" is implemented with a field to track initialization, the simple solution is to wrap the lazy val with a mutable reference. You'll have to deal with mutability of that reference, of course.

scala> class V { lazy val v = System.currentTimeMillis }
defined class V

scala> @volatile var x = new V
x: V = V@77468bd9

scala> x.v
res0: Long = 1431212391127

scala> x.v
res1: Long = 1431212391127

scala> x = new V
x: V = V@28f67ac7

scala> x.v
res2: Long = 1431212407970

and then something like

scala> import concurrent._, duration._, ExecutionContext.Implicits.global
import concurrent._
import duration._
import ExecutionContext.Implicits.global

scala> implicit class ex(val d: Deadline) extends AnyVal { def expiring(f: => Unit) =
     | Future(Await.ready(Promise().future, d.timeLeft)) onComplete (_ => f) }
defined class ex

scala> 10 seconds fromNow expiring { x = new V }

scala> x.v
res4: Long = 1431212407970

scala> x.v
res5: Long = 1431212407970

scala> x.v
res6: Long = 1431213010180
like image 160
som-snytt Avatar answered Jan 05 '23 11:01

som-snytt


You are running into two problems:

  • val is immutable, i.e. cannot be changed after initialization
  • even if you used var instead, it has no mechanism to expire after some time

My suggestion would be turn getDayOfWeek into a function that tracks the current value and state in some other instance:

private var dayOfWeek: Option[Day] = None
private var lastEvaluated: Int = 0

def getDayOfWeek = dayOfWeek match {
  case None => expensiveGetDayOfWeek
  case Some(day) =>
    if (DateTime.now.getDayOfYear > lastEvaluated) expensiveGetDayOfWeek
    else day

}

private def expensiveGetDayOfWeek: Day = {
  dayOfWeek = Some(someExpensiveOperation())
  lastEvaluated = DateTime.now.getDayOfYear
  dayOfWeek.get
}

My state tracking of when to recompute is a bit hacky, so you'll want to come up with your own, but the basic gist is that you calculate on demand.

like image 37
Arne Claassen Avatar answered Jan 05 '23 12:01

Arne Claassen