Lazy Val - How to reset the value?
I can use an expensive method and return the result depending on the side effects. For example, depending on time of day / week and Monte Carlo simulation of quantum chronodynamics. Because it is expensive and I may not need it, I will use Scalaslazy val
lazy val maybeUnusedValue = getDayOfWeek
Now my program is still running 12 hours. I want to repeat the calculation because my day may have changed during this time.
Is there an easy way to force Scala to revert maybeUnusedValue
back to an uninitialized state, and therefore forcing it to be recalculated for the next use?
source to share
Is there an easy way
Since "laziness" is implemented with a field to keep track of initialization, a simple solution is to wrap the lazy val with a mutable reference. You will of course have to deal with the volatility of this link.
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
source to share
You have two problems:
-
val
immutable, that is, it cannot be changed after initialization - even if you used it
var
instead, it has no mechanism to expire after a while
My suggestion would turn getDayOfWeek
into a function that keeps track of 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 for when to overcommit is a bit of a hack so you have to come up with your own, but the main point is that you compute on demand.
source to share