2015-07-31 6 views
9

Zauważyłem, że obliczeniowe lazy val powtarza kilka razy (w przypadku wyjątku):Czy to prawidłowe zachowanie, że `lazy val` działa jak` def` w przypadku wyjątku?

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 

Nie powinno być tak:

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 
+2

Tak właśnie powinienem przeczytać, powinno to działać jakiś czas temu. –

Odpowiedz

5

W this po wyjaśniają one bardzo dobrze, jak jest lazy val skompilowany przez kompilator Scala. Zasadniczo, jeśli ocena wyrażenia nie powiedzie się, sygnalizacja bitów wskaźnika, że ​​lazy val zawiera dane, nie zostanie ustawiona.

update1:

myślę jeden z powodów dzieje z pierwszym podejściem może być, że druga może być emulowane za pomocą dwóch lazy val s, bez obciążania leżących u podstaw realizacji z wieloma zmiennymi lotnych:

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:

Jak @Silly Freak zaproponowała w swoim komentarzu,

scala> lazy val _aaa = Try {println("calc"); sys.error("aaaa")} 
_aaa: scala.util.Try[Nothing] = <lazy> 

scala> def aaa = _aaa.get 
aaa: Nothing 

może działać jeszcze lepiej, ponieważ możemy uniknąć konieczności posiadania dwóch lazy val s.

+0

Już wiesz o tym, jak to działa. Moje pytanie dotyczy poprawności podejścia lub może lepszego podejścia? Jak sądzę, nic nie powstrzymuje ich (jak ja to widzę) przed wprowadzeniem poprawnej obsługi wyjątków wewnątrz leniwego komputera. – dk14

+0

Mam na myśli, że mogą mieć inny bit do isFailure (przynajmniej) lub pamiętać wyjątek (co najwyżej, ale droższy). Na razie '0' oznacza zarówno niepowodzenie/niezainicjalizowane, więc nie można ich rozróżnić. – dk14

+1

Nie mogę tego tak naprawdę powiedzieć, ale prawdopodobnie ze względu na wydajność (https://twitter.com/djspiewak/status/302489756552536064). Dla mnie oba podejścia wydają się rozsądne. Chociaż nie jest to dokładnie to samo, ale w zależności od twojego przypadku użycia, możesz emulować swoje podejście, wykonując 'lazy val aaa = Try {println (" calc "); sys.error ("aaaa")} ' – kosii

Powiązane problemy