ilango
ilango

Reputation: 1287

Trying to understand how classes declared on the REPL are treated internally

I am trying to understand how scoping works in the REPL. I tried following Section 5.1.1 of Joshua Suereth's book Scala in depth.This is on Windows XP, Java 7 and Scala 2.9.1. I declare a class Dinner in the REPL. The binding Dinner exists in the local scope. Then I instantiate because it is locally bound.

scala> class Dinner {
 | val veggie="broccoli"
 | def announceDinner(veggie: String){
 | println("Dinner happens to be tasteless " + veggie + " soup")
 | }
 | }
defined class Dinner

scala> new Dinner
res1: Dinner = Dinner@27fb77

So far so good. The name Dinner was bound locally and we could also construct a val x that could hold a reference to new Dinner.

From what I know so far, The REPL will wrap the above code in objects internally. Okay, my knowledge of Scala is not quite as deep yet and I am trying to understand how Class might be internally wrapped by the REPL.

Is there an REPL command that can help me evaluate these objects?

Upvotes: 3

Views: 250

Answers (1)

pedrofurla
pedrofurla

Reputation: 12783

Here goes a very quick and dirty way to what is going on in the REPL.

Invoke the REPL with scala -Xprint:typer

scala> class Dinner {
     |   val veggie="broccoli"
     |   def announceDinner(veggie: String){
     |   println("Dinner happens to be tasteless " + veggie + " soup")
     |   }
     |  }
[[syntax trees at end of typer]]// Scala source: <console>
package $line1 {
  final object $read extends java.lang.Object with ScalaObject {
    def this(): object $line1.$read = {
      $read.super.this();
      ()
    };
    final object $iw extends java.lang.Object with ScalaObject {
      def this(): object $line1.$read.$iw = {
        $iw.super.this();
        ()
      };
      final object $iw extends java.lang.Object with ScalaObject {
        def this(): object $line1.$read.$iw.$iw = {
          $iw.super.this();
          ()
        };
        class Dinner extends java.lang.Object with ScalaObject {
          def this(): $line1.$read.$iw.$iw.Dinner = {
            Dinner.super.this();
            ()
          };
          private[this] val veggie: java.lang.String = "broccoli";
          <stable> <accessor> def veggie: java.lang.String = Dinner.this.veggie;
          def announceDinner(veggie: String): Unit = scala.this.Predef.println("Dinner happens to be tasteless ".+(veggie).+(" soup"))
        }
      }
    }
  }
}

[[syntax trees at end of typer]]// Scala source: <console>
package $line1 {
  final object $eval extends java.lang.Object with ScalaObject {
    def this(): object $line1.$eval = {
      $eval.super.this();
      ()
    };
    private[this] val $print: String = {
      $read.$iw.$iw;
      "defined class Dinner\012"
    };
    <stable> <accessor> def $print: String = $eval.this.$print
  }
}

defined class Dinner

As you can check above Dinner ends up wrapped into $line1.$read.$iw.$iw. Now let's see what happens next:

[[syntax trees at end of typer]]// Scala source: <console>
package $line2 {
  final object $read extends java.lang.Object with ScalaObject {
    def this(): object $line2.$read = {
      $read.super.this();
      ()
    };
    final object $iw extends java.lang.Object with ScalaObject {
      def this(): object $line2.$read.$iw = {
        $iw.super.this();
        ()
      };
      import $line1.$read.$iw.$iw.Dinner;
      final object $iw extends java.lang.Object with ScalaObject {
        def this(): object $line2.$read.$iw.$iw = {
          $iw.super.this();
          ()
        };
        private[this] val res0: $line1.$read.$iw.$iw.Dinner = new $line1.$read.$iw.$iw.Dinner();
        <stable> <accessor> def res0: $line1.$read.$iw.$iw.Dinner = $iw.this.res0
      }
    }
  }
}

[[syntax trees at end of typer]]// Scala source: <console>
package $line2 {
  final object $eval extends java.lang.Object with ScalaObject {
    def this(): object $line2.$eval = {
      $eval.super.this();
      ()
    };
    lazy private[this] var $result: $line1.$read.$iw.$iw.Dinner = {
      $eval.this.$print;
      $line2.$read.$iw.$iw.res0
    };
    private[this] val $print: String = {
      $read.$iw.$iw;
      "res0: $line1.$read.$iw.$iw.Dinner = ".+(scala.runtime.ScalaRunTime.replStringOf($line2.$read.$iw.$iw.res0, 1000))
    };
    <stable> <accessor> def $print: String = $eval.this.$print
  }
}

Basically the same thing as before but using $line2 instead of $line1. Notice the import $line1.$read.$iw.$iw.Dinner right before $line2.$read.$iw.$iw.

This way we can see why defining companion objects in two different lines doesn't work, they end up wrapped into different objects and companions need to be defined in the same scope/source file.

Upvotes: 3

Related Questions