I've noticed that lazy val
repeats computation several times (in case of exception):
scala> lazy val aaa = {println("calc"); sys.error("aaaa")}
aaa: Nothing = <lazy>
scala> aaa
calc
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at .aaa$lzycompute(<console>:7)
at .aaa(<console>:7)
... 33 elided
scala> aaa
calc
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at .aaa$lzycompute(<console>:7)
at .aaa(<console>:7)
... 33 elided
Shouldn't it be like:
scala> aaa
calc
java.lang.RuntimeException: Not Initialized!
caused by
java.lang.RuntimeException: aaaa
scala> aaa
java.lang.RuntimeException: Not Initialized!
caused by
java.lang.RuntimeException: aaaa
In this post they explain very well how lazy val
is compiled by the Scala compiler. Basically, if the evaluation of the expression fails, then the indicator-bit signaling that the lazy val
contains its data won't be set.
update1:
I think one reason going with the first approach could be that the second one may be emulated by using two lazy val
s, without burdening the underlying implementation with multiple volatile variables:
scala> lazy val _aaa = Try {println("calc"); sys.error("aaaa")}
_aaa: scala.util.Try[Nothing] = <lazy>
scala> lazy val aaa = _aaa.get
aaa: Nothing = <lazy>
scala> aaa
calc
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at $anonfun$_aaa$1.apply(<console>:10)
at $anonfun$_aaa$1.apply(<console>:10)
at scala.util.Try$.apply(Try.scala:191)
at ._aaa$lzycompute(<console>:10)
at ._aaa(<console>:10)
at .aaa$lzycompute(<console>:11)
at .aaa(<console>:11)
... 33 elided
scala> aaa
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at $anonfun$_aaa$1.apply(<console>:10)
at $anonfun$_aaa$1.apply(<console>:10)
at scala.util.Try$.apply(Try.scala:191)
at ._aaa$lzycompute(<console>:10)
at ._aaa(<console>:10)
at .aaa$lzycompute(<console>:11)
at .aaa(<console>:11)
... 33 elided
update2:
As @Silly Freak has proposed in his comment,
scala> lazy val _aaa = Try {println("calc"); sys.error("aaaa")}
_aaa: scala.util.Try[Nothing] = <lazy>
scala> def aaa = _aaa.get
aaa: Nothing
may work even better, as we can avoid having two lazy val
s.