mbrambley
mbrambley

Reputation: 245

nested/inner scope of variables

What's the exact deal with scope regarding variables in Scala?

When I open curly brackets I can still access the value of the outer vars (and modify them if vars):

scala> var mmm = 4
mmm: Int = 4

scala> {
     | println(mmm)
     | mmm += 2
     | println(mmm)
     | }
4
6

scala> println(mmm)
6

But Odersky says on page 180 or his book that

In a Scala program, an inner variable is said to shadow a like-named outer variable, because the outer variable becomes invisible in the inner scope.

This seems even weirder:

scala> val a = 4
a: Int = 4

scala> {
     | println(a)
     | }
4

So do I get a copy of it created in the inner scope?

scala> val a = 4
a: Int = 4

scala> {
     | val a = 8
     | }

Why can I say val again if its immutable?

scala> val a = 4
a: Int = 4

scala> {
     | println(a)
     | val a = 8
     | println(a)
     | }

But for this one I get an error:

error: forward reference extends over definition of value a
              println(a)

Upvotes: 1

Views: 4306

Answers (2)

Rex Kerr
Rex Kerr

Reputation: 167891

If you create a new one in the block, the new one shadows the old (outer) one. If you don't, you refer to the outer one.

It doesn't matter where in the block the new one is created; if it appears anywhere, it shadows the outer one.

So,

val a = 5
{ println(a) }  // This is outer a

val a = 5
{ val a = 8; println(a) }  // This is inner a

val a = 5
{ println(a); val a = 8 }  // This is broken
                           // you try to print the inner a
                           // but it doesn't exist yet

Edit: let's unpack the last one

val a = 5
// Okay, I have something called a

{  // Oh, new block beginning!  Maybe there are local variables
  println(a)
  val a = 8    // Yeah, there's one!
               // And it's got the same name as the outer one
               // Oh well, who needs the outer one anyway?
}

// Waitaminute, what was that block supposed to do?
{
  println(a)   // Inner block has an a, so this must be the inner a
  val a = 8    // Which is 8
}

// Hang on, operations happen in order
{
  println(a)   // What inner a?!

// Abort, abort, abort!!!!

Upvotes: 5

Péter Török
Péter Török

Reputation: 116266

Blocks don't change the visibility of outer variables per se. Only if you declare variables with the same name within the inner scope, as the Odersky quote explains.

Your first two examples don't do this, so the outer variable is plainly visible within the inner scope, and Odersky's quote doesn't apply. The a in the inner scope is the same as the a in the outer scope, there is no copying involved.

In the third example however, the two as are two independent val variables, i.e. the inner a is not redefining the outer a, just shadowing it as per Odersky's quote, e.g.:

val a = 4
println(a)  // prints 4 - outer a
{
  val a = 8;
  println(a)  // prints 8 - inner a
}
println(a)  // prints 4 - outer a in scope again

I.e. after exiting the inner block, you see that the outer a with its original value is still there.

However, the code you show in the third example is highly ambiguous, as it is hard to decide without deeper knowledge of the language specification, which variable your first println(a) statement refers to. I don't have an exact reference at hand but I believe such code is disallowed by the compiler as this style provides no benefit while it is inherently error prone.

Upvotes: 1

Related Questions