Evil Ipos
Evil Ipos

Reputation: 61

Strange behaviour

case class Test(kind: Int) {
  val ifX = if (isX) "is X" else "not X"
  val isX = kind == 1
}

val test = Test(1)
println("ifX=%s, isX=%b".format(test.ifX, test.isX))

Why this code print: ifX=not X, isX=true

When is move "val ifX" before "ifX" it's ok (print ifX=is X)

EDIT: I know how to fix that. I can't understand why compiler not issue warning or error in this situation.

Upvotes: 3

Views: 182

Answers (4)

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297155

OMG, I wonder how many times we'll go through this...

It's not possible to statically detect incorrectly used forward references in all cases in something better than exponential time. Or, if it is, it's complex enough that no one has done it.

This particular case is simple enough -- there's no inheritance, no traits being extended, no early initializers, no method being called, no closures, no functions, no nothing. It's simple enough that a warning may be added in the future.

You can catch it at run-time, and Scala provides the -Xcheckinit flag for that.

By the way, you can get the exact same problem in Java, though it does warn you in this particular case.

Upvotes: 7

Chris Shain
Chris Shain

Reputation: 51319

You are making the assumption that order doesn't matter in scala. It does. Because isX is a variable value, and while it's defined when ifX runs, it's value is still uninitialized, and so it is the default for its type (boolean, so false).

If you redefine isX as a function (def isX = ...) it would work.

This is roughly equivalent to the following Java:

class Test {
    String ifX;
    bool isX; // Defaults to false, its a primitive after all
    public Test(Int kind) {
        ifX = isX ? "is X" : "not X";
        isX = kind == 1;
    }
}

Upvotes: 7

Jens Egholm
Jens Egholm

Reputation: 2710

As Chris Shain pointed out isX is not defined yet. I just want to add that it's not a variable, but a value, which means that you can actually make it lazy. A lazy value is only instantiated when you need it. Both ifX and isX could be made lazy in theory. Try something like this:

case class Test(kind: Int) {
   val ifX = if (isX) "is X" else "not X"
   lazy val isX = kind == 1
 }

That should give the desired output.

Upvotes: 7

FMaz008
FMaz008

Reputation: 11285

well, isX is undefined when the class is ran the first time. Can you just switch both lines ?

Upvotes: 1

Related Questions