3

What do you think prints out?

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

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

Answer:

foonull
3

Why? Is there a part in specification that describes/explains this?

EDIT: To clarify my astonishment - I do realize that foo is undefined at val foo: String = "foo" + foo and that's why it has a default value null (zero for integers). But this doesn't seem very "clean", and I see opinions here that agree with me. I was hoping that compiler would stop me from doing something like that. It does make sense in some particular cases, such as when defining Streams which are lazy by nature, but for strings and integers I would expect either stopping me due to reassignment to val or telling me that I'm trying to use an undefined value, just like as if I wrote val foo = whatever (given that whatever was never defined).

To further complicate things, @dk14 points out that this behaviour is only present for values represented as fields and doesn't happen within blocks, e.g.

val bar: String = {
  val foo: String = "foo" + foo // error: forward reference extends...
  "bar"
}
slouc
  • 9,508
  • 3
  • 16
  • 41
  • What behaviour would you expect? – Jasper-M Nov 04 '16 at 14:41
  • 1
    Compiler telling me "reassigning to val" or something like that. Or telling me that it doesn't know what `foo` is (because compiler's lexical analysis didn't yet store this token since it wasn't fully defined yet). Definitely not giving it temporary value `null` which sounds more like JavaScript than Scala (with undefined instead of null of course). – slouc Nov 04 '16 at 14:45
  • @slouc also notice the difference in behavior when replacing the `val`s with `def`s. – fxlae Nov 04 '16 at 16:41
  • 1
    @fxlae Of course, and that's completely logical. But `val`s behaving this way seems a bit "broken". I would like to trust the `val`s to be really immutable, which means that `foo` either has some never-changing value or doesn't exist at all. Not to have value null here and xy there. – slouc Nov 04 '16 at 17:11

4 Answers4

11

Yes, see the SLS Section 4.2.

foo is a String which is a reference type. It has a default value of null. foo2 is an Int which has a default value of 0.

In both cases, you are referring to a val which has not been initialized, so the default value is used.

Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
  • OK, so it's the same as if e.g. you are trying to use a field within some abstract class whose value has not been yet defined in the subclass (due to order of linearization). But shouldn't this definition of `val` be caught by the compiler? It's a clear re-declaration of a `val`. – slouc Nov 04 '16 at 14:33
  • It's not a re-declaration of a val. The val is only declared once, but it's being used before it has been initialized. – Michael Zajac Nov 04 '16 at 14:45
  • 1
    Exactly, and compiler could have warned us about this. It should be the same as saying `val foo = whatever` (given that `whatever` is not defined anywhere). But I guess what happens is "OK, here we have initialization of a val, let's create it and assign it a null value until we finish the initialization". – slouc Nov 04 '16 at 14:49
  • 1
    @slouc hear ya, unfortunately, scala is not as safe as let's say Haskell because of compromises with Java. There is a safe subset called Scalazzi and some of sbt/scalac-plugins can give you more warnings/errors: https://github.com/puffnfresh/wartremover – dk14 Nov 05 '16 at 08:52
1

Scala's Int is backed by a primitive type (int) underneath, and its default value (before initialization) is 0.

On the other hand, String is an Object which is indeed null when not initialized.

Shastick
  • 1,218
  • 1
  • 12
  • 29
1

In both cases foo resp. foo2 have their default values according to the JVM specification. That's null for a reference type and 0 for an int or Int - as Scala spells it.

jmg
  • 7,308
  • 1
  • 18
  • 22
1

Unfortunately, scala is not as safe as let's say Haskell because of compromises with Java's OOP model ("Object-oriented meets Functional"). There is a safe subset of Scala called Scalazzi and some of sbt/scalac-plugins can give you more warnings/errors:

https://github.com/puffnfresh/wartremover (doesn't check for the case you've found though)

Returning back to your case, this happens only for values represented as fields and doesn't happen when you're inside a function/method/block:

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}

The reason of this situation is integration with Java as val compiles to the final field in JVM, so Scala saves all initialization specifics of JVM classes (I believe it's part of JSR-133: Java Memory Model). Here is the more complicated example that explains this behavior:

 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, here you can see the warning that you didn't see in the first place.

dk14
  • 22,206
  • 4
  • 51
  • 88
  • @slouc you're welcome. I added clarification about why this behaviour is taking place – dk14 Nov 05 '16 at 11:08
  • Great, thanks. I already accepted an answer before you came along, but I do admit this one is more informative. – slouc Nov 05 '16 at 11:30