New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullPointerException when re-defining a val set in the context of itself #9241

Closed
scabug opened this Issue Mar 21, 2015 · 5 comments

Comments

Projects
None yet
1 participant
@scabug
Copy link

scabug commented Mar 21, 2015

Background:
Odersky's book, "+Programming in Scala, Second Edition+", Chapter 2, Footnotes, reads:
[5] In the interpreter, however, you can define a new val with a name that was already used before....

Test Case:

val mySet = Set("Item1","Item2")
val mySet: Set[String] = mySet + "Item3"

Expected Output:

mySet: Set[String] = Set(Item1, Item2, Item3)

Actual Output:

java.lang.NullPointerException
        at $line40.$read$$iw$$iw$.<init>(<console>:8)
        at $line40.$read$$iw$$iw$.<clinit>(<console>)
        at $line40.$eval$.$print$lzycompute(<console>:7)
        at $line40.$eval$.$print(<console>:6)
        at $line40.$eval.$print(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:773)
        at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:1020)
        at scala.tools.nsc.interpreter.IMain$WrappedRequest$$anonfun$loadAndRunReq$1.apply(IMain.scala:627)
        at scala.tools.nsc.interpreter.IMain$WrappedRequest$$anonfun$loadAndRunReq$1.apply(IMain.scala:626)
        at scala.reflect.internal.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
        at scala.reflect.internal.util.AbstractFileClassLoader.asContext(AbstractFileClassLoader.scala:19)
        at scala.tools.nsc.interpreter.IMain$WrappedRequest.loadAndRunReq(IMain.scala:626)
        at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:558)
        at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:554)
        at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:804)
        at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:849)
        at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:707)
        at scala.tools.nsc.interpreter.ILoop.processLine(ILoop.scala:405)
        at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:431)
        at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:920)
        at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:906)
        at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:906)
        at scala.reflect.internal.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:97)
        at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:906)
        at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:74)
        at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:87)
        at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:98)
        at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
        at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

Additional notes:
Running the below code:

mySet + "Item3"

...results in the following output:

scala.collection.immutable.Set[String] = Set(Item1, Item2, Item3)

It is only when we try to redefine mySet as this output, as described in the book, that we encounter issues.

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Mar 21, 2015

Imported From: https://issues.scala-lang.org/browse/SI-9241?orig=1
Reporter: Jordan Pilat (jrpilat)
Affected Versions: 2.11.6

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Mar 22, 2015

@som-snytt said:
Recursive definitions are allowed and are confusing enough to be discussed at length in the usual forums.

The book does not propose it as a helpful example at the citation.

Footnotes are at the end of the section.

The real problem lies in the forward-referenced section. Is no one bothered by the semi-colons? Are we barbarians?

Civilized people use locally to disambiguate their braces.

Also, the explanation of scoping in the REPL, albeit merely a "conceptual visualization", i.e., a metaphor, is a bit imprecise.

I would expect the stack trace to be truncated:

scala> val mySet: Set[String] = mySet + "Item3"
java.lang.NullPointerException
  ... 33 elided

@scabug scabug closed this Mar 22, 2015

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Mar 22, 2015

Jordan Pilat (jrpilat) said (edited on Mar 22, 2015 5:25:40 AM UTC):
The stack trace was indeed truncated, however the instructions for bug reporting instructed me to include the full stack trace. I ought've included both.

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Mar 22, 2015

Jordan Pilat (jrpilat) said:
Given that this is marked as not a bug, how should the book read instead?

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Mar 22, 2015

@som-snytt said:
The behavior of the second line has nothing at all to do with the first line. The book is correct.

The variable on the RHS refers to the variable on the LHS being defined, hence it is recursive. You can google it. I am far too lazy for such an undertaking.

The REPL will not actually bring the first variable into scope at all, which is slightly different from what the book says; such a distinction may only matter with certain imports.

Actually, that's not true. For some reason, it pulls in the import. But the mechanism is known to be imprecise. As a rule, it tries to import only what it needs from history.

scala> val mySet: Set[String] = mySet + "Item3"
[[syntax trees at end of                     typer]] // <console>
package $line2 {
  object $read extends scala.AnyRef {
    def <init>(): $line2.$read.type = {
      $read.super.<init>();
      ()
    };
    object $iw extends scala.AnyRef {
      def <init>(): type = {
        $iw.super.<init>();
        ()
      };
      import $line1.$read.$iw.$iw.mySet;
      object $iw extends scala.AnyRef {
        def <init>(): type = {
          $iw.super.<init>();
          ()
        };
        private[this] val mySet: Set[String] = $iw.this.mySet.+("Item3");
        <stable> <accessor> def mySet: Set[String] = $iw.this.mySet
      }
    }
  }
}

@scabug scabug added the repl label Apr 7, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment