2016-11-04 14 views
3

Was denken Sie, druckt aus?Scala rekursives Val-Verhalten

val foo: String = "foo" + foo 
println(foo) 

val foo2: Int = 3 + foo2 
println(foo2) 

Antwort:

foonull 
3 

Warum? Gibt es einen Teil in der Spezifikation, der dies beschreibt/erklärt?

EDIT: mein Erstaunen Um zu klären - ich erkennen, dass foo bei val foo: String = "foo" + foo nicht definiert ist, und das ist, warum es einen Standardwert null (Null für ganze Zahlen) hat. Aber das scheint nicht sehr "sauber" zu sein, und ich sehe hier Meinungen, die mir zustimmen. Ich hatte gehofft, dass der Compiler mich davon abhalten würde, so etwas zu machen. Es macht in einigen besonderen Fällen Sinn, wie zum Beispiel bei der Definition von 0's, die von Natur aus faul sind, aber für Strings und ganze Zahlen würde ich entweder stoppen, weil ich auf val umgestellt habe, oder mir sagen, dass ich versuche, einen undefinierten Wert zu verwenden , als ob ich schrieb val foo = whatever (vorausgesetzt, dass whatever wurde nie definiert).

Um weitere Dinge zu komplizieren, weist @ dk14 darauf hin, dass dieses Verhalten nur für Werte vorhanden ist, die als Felder dargestellt werden und nicht innerhalb von Blöcken auftreten, z.

val bar: String = { 
    val foo: String = "foo" + foo // error: forward reference extends... 
    "bar" 
} 
+0

Welches Verhalten würden Sie erwarten? –

+1

Compiler, der mir sagt, dass er "val" oder etwas ähnlichem zuweist. Oder er sagt mir, dass es nicht weiß, was "foo" ist (weil die lexikalische Analyse des Compilers dieses Token noch nicht gespeichert hat, da es noch nicht vollständig definiert war). Definitiv keinen temporären Wert 'null' geben, der mehr wie JavaScript als Scala klingt (mit undefiniertem statt natürlichem null). – slouc

+0

@slouc bemerken auch den Unterschied im Verhalten beim Ersetzen der 'Val' mit 'def's. – fxlae

Antwort

11

Ja, siehe SLS Section 4.2.

foo ist ein String, der ein Referenztyp ist. Es hat einen Standardwert von null. foo2 ist ein Int, der einen Standardwert von 0 hat.

In beiden Fällen beziehen Sie sich auf einen Wert, der nicht initialisiert wurde, so dass der Standardwert verwendet wird.

+0

OK, also ist es das gleiche als wenn z.B. Sie versuchen, ein Feld innerhalb einer abstrakten Klasse zu verwenden, dessen Wert in der Unterklasse noch nicht definiert ist (aufgrund der Reihenfolge der Linearisierung). Aber sollte diese Definition von "val" nicht vom Compiler abgefangen werden? Es ist eine klare Wiedererklärung eines "val". – slouc

+0

Es ist keine erneute Deklaration eines Val. Das val wird nur einmal deklariert, aber es wird verwendet, bevor es initialisiert wurde. –

+1

Genau, und der Compiler hätte uns davor warnen können. Es sollte dasselbe sein wie "val foo = was auch immer" (vorausgesetzt, dass "was auch immer" nirgendwo definiert ist). Aber ich denke, was passiert ist "OK, hier haben wir die Initialisierung eines Val, lassen Sie uns es schaffen und es einen Nullwert zuweisen, bis wir die Initialisierung abgeschlossen haben". – slouc

1

Scala Int durch eine primitive Art gesichert wird (int) unterhalb und sein Standardwert (vor der Initialisierung) ist 0.

Auf der anderen Seite ist String ein Object die in der Tat ist null, wenn sie nicht initialisiert.

1

In beiden Fällen foo resp. foo2 haben ihre Standardwerte gemäß der JVM-Spezifikation. Das ist null für einen Referenztyp und 0 für einen int oder Int - wie Scala es buchstabiert.

+0

Es ist eigentlich das JMM (Java Memory Model), das das definiert – Ven

1

Scala ist leider nicht so sicher wie Haskell wegen der Kompromisse mit dem OOP-Modell von Java ("Object-oriented meets Functional"). Es ist eine sichere Teilmenge von Scala genannt Scalazzi und einige von SBT/scalac-Plugins können Sie mehrere Warnungen/Fehler geben:

https://github.com/puffnfresh/wartremover

Rückkehr zurück zu Ihrem Fall (doesn't check für den Fall, dass Sie obwohl gefunden habe) dies geschieht nur für Werte als Felder dargestellt und geschieht nicht, wenn Sie in einer Funktion/Methode/Block sind:

scala> val foo2: Int = 3 + foo2 
foo2: Int = 3 

scala> {val foo2: Int = 3 + foo2 } 
<console>:14: error: forward reference extends over definition of value foo2 
     {val foo2: Int = 3 + foo2 } 
          ^

scala> def method = {val a: Int = 3 + a} 
<console>:12: error: forward reference extends over definition of value a 
     def method = {val a: Int = 3 + a} 

der Grund für diese Situation ist die Integration mit Java als val zum final Feld kompiliert in JVM, also speichert Scala alle Initialisierungen n Besonderheiten von JVM-Klassen (ich glaube, dass es Teil von JSR-133 ist: Java Memory Model).Hier ist das kompliziertere Beispiel, das dieses Verhalten erklärt:

scala> object Z{ val a = b; val b = 5} 
<console>:12: warning: Reference to uninitialized value b 
    object Z{ val a = b; val b = 5} 
        ^
defined object Z 

scala> Z.a 
res12: Int = 0 

So, hier können Sie die Warnung sehen, dass Sie nicht an erster Stelle gesehen haben.

+2

Wow, Danke, dass Sie auf die Situation in einem Block hingewiesen haben. – slouc

+0

@slouc du bist willkommen. Ich fügte hinzu, warum dieses Verhalten stattfindet – dk14

+0

Großartig, danke. Ich habe bereits eine Antwort angenommen, bevor du mitgehst, aber ich gebe zu, dass dies informativer ist. – slouc