22

Why isn't it possible to have this:

def main(args:Array[String]) {
    val whatever:String // Have it uninitialized here

    if(someCondition) {
        whatever = "final value" // Initialize it here
    }
}

I don't understand why this shouldn't be legal. I know that I can make it a var, but why do we have to initialize the val exactly when we declare it? Doesn't it seem more logical to be able to initialize it later?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Geo
  • 93,257
  • 117
  • 344
  • 520
  • How would the compiler make sure it is indeed intialized? Yes, it's possible - but hard, and not needed. –  Oct 26 '10 at 18:10
  • 2
    Because up to the point of that initialization, the value is *undefined* and just makes sense in terms of a following control flow. In functional programming, there are no undefined values - and values - not states - are the only things that matter. – Dario Oct 26 '10 at 18:16
  • Well, basically there is no "uninitialized". The JVM doesn't support this concept. In Java these "uninitialized" fields have the value `null`. Because of the way class initialization works, it is sometimes possible to access fields and get bogus nulls back. This lead to code which looks unsuspicious, but throws `NPE` s at runtime. Serialization/RMI adds more problems to that approach. If you are interested, look at some examples in "Java Puzzlers". Basically: Scala prevents weird things from happening and reduces the need for uninitialized values, because everything is an expression. – soc Oct 27 '10 at 09:38

5 Answers5

39

You can do:

  val whatever =
    if (someCondition)
      "final value"
    else
      "other value"
Nicolas Payette
  • 14,847
  • 1
  • 27
  • 37
  • 1
    And if someCondition needs to be used to set more than one value, you can use tuples and value decomposition. – andresp Sep 16 '17 at 13:36
29

The Java solution is actually a workaround to the problem that not all expressions return values, so you can't write this in Java:

final String whatever = if (someCondition) {
    "final value"
} else {
    "other value"
}

Increasingly, the trend in Java is to use the ternary operator instead:

final String whatever = someCondition ? "final value" : "other value"

Which is fine for that limited use case, but totally untenable once you start dealing with switch statements and multiple constructors.


Scala's approach is different here. All object construction must ultimately pass through a single "primary" constructor, all expressions return a value (even if it's Unit, equivalent to Java's Void), and constructor injection is strongly favoured. This results in object graphs being cleanly built as a Directed Acyclic Graph, and also works very nicely with immutable objects.

You also want to be aware that declaring and defining variables in separate operations is, in general, a bad practice when dealing with multiple threads - and could leave you vulnerable to exposing nulls and race conditions when you least expect them (though this isn't really a problem during object construction). The atomic creation of immutable values is just one aspect of the way in which functional languages help to to deal with concurrency.

It also means that the Scala compiler can avoid some of the hideously complicated flow analysis from the Java language spec.

As previously stated, the Scala Way™ is:

val whatever =
  if (someCondition)
    "final value"
  else
    "other value"

An approach which also scales up to other control structures:

val whatever = someCondition match {
  case 1 => "one"
  case 2 => "two"
  case 3 => "three"
  case _ => "other"
}

With a bit of Scala experience you'll discover that this style helps the compiler to help you, and you should find yourself writing programs with fewer bugs!

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
9

Use lazy vals like so:

def main(args:Array[String]) {
  lazy val whatever:String = if (someCondition) "final value" else "other value"

  // ...

  println(whatever) // will only initialize on first access
}
Max A.
  • 4,842
  • 6
  • 29
  • 27
  • 1
    The assignment inside the if will return Unit. An if without an else clause will also return Unit. I think you example won't compile... – Nicolas Payette Oct 26 '10 at 19:17
7

In addition to what others have said, note that Java allows "blank final" "variables", which is a feature I kinda miss:

final Boolean isItChristmasYet;

if (new Date().before(christmas)) {
    isItChristmasYet = Boolean.FALSE;
} else {
    isItChristmasYet = Boolean.TRUE;
}

However, thanks to data flow analysis in the compiler, javac wouldn't let you leave your whatever variable unassigned if someCondition doesn't hold.

Alex Cruise
  • 7,939
  • 1
  • 27
  • 40
  • Yeah, this is what I miss from Java too. – Geo Oct 26 '10 at 18:42
  • 2
    Out of curiosity, what exactly do you gain by having the uninitialized variable pre-declared? It's not as if you could use it in any way before it's actually initialized... – Nicolas Payette Oct 26 '10 at 20:06
  • It's a stylistic thing, I used to declare all my locals up-front at the top of their scope. I probably still would--at least sometimes--if Scala had blank vals. :) – Alex Cruise Oct 26 '10 at 20:53
  • 2
    What's wrong with `final boolean isItChristmasYet = ! (new Date().before(christmas))` ? – Kevin Wright Oct 27 '10 at 15:44
  • I knew someone would golf me. :) Nothing, but this is a trivial example in a style that's suited to more complex situations. – Alex Cruise Oct 27 '10 at 23:06
3

Because the purpose of 'val' is to signal to the reader (and the compiler): "This value will stay what it is initialized to until it goes out of existence"

This doesn't make much sense without initialization.

Of course one could dream up somehting like val(3) which allows three assignement to a variable, but I don't think that would be of much use.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348