Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it correct behaviour that `lazy val` acts like `def` in case of exception?

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  
like image 607
dk14 Avatar asked Jul 31 '15 09:07

dk14


People also ask

When would you use a lazy Val in your Scala program instead of a regular Val?

When we use lazy keyword before a val keyword, a block expression is used to initialize the variable – “geek”. For using the block in Scala, the sequence of expressions is enclosed in { }. So, the value “12” is the value of block and the assignment expression value is Unit which is actually a void type in Java.

What is strict Val vs lazy Val in Scala?

val evaluates as soon as you initialise the object and stores the result. lazy val evaluates the first time that it's accessed and stores the result. def executes the piece of code every time – pretty much like a Java method would.

How does lazy Val work in Scala?

How Does a lazy val Work ? The compiler does not immediately evaluate the bound expression of a lazy val. It evaluates the variable only on its first access. Upon initial access, the compiler evaluates the expression and stores the result in the lazy val.


1 Answers

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 vals, 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 vals.

like image 184
kosii Avatar answered Sep 17 '22 12:09

kosii