Scala Programming Language
  1. Scala Programming Language
  2. SI-4856

vals are accessible before they are initialized, and their value is sometimes null or zero if accessed at that time, and no warning or error is issued upon their use

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Trivial Trivial
    • Resolution: Fixed
    • Affects Version/s: Scala 2.9.0
    • Fix Version/s: None
    • Component/s: Specification
    • Labels:
      None

      Description

      The behavior or forward references to vals and recursive values is a bit odd and inconsistent.


      1.

      In a class initializer a val can refer to a val not yet declared and no error or warning is issued, instead the value is treated as zero or null.

      For example:

      
      scala> :paste
      // Entering paste mode (ctrl-D to finish)
      
      class Foo {
      val bar = x+1
      val x = 5
      }
      
      // Exiting paste mode, now interpreting.
      
      defined class Foo
      
      scala> val foo = new Foo
      foo: Foo = Foo@7875455c
      
      scala> foo.bar
      res0: Int = 1
      

      Int the end the value of x is treated as zero unexpectedly. I would expect either foo.bar to 6 or give an error at compile time or run time. This could easily create bugs where someone reordered vals in the initializer such a way that some of them would now be null or zero.


      2.

      You can use lazy vals and by-name parameters to create an object that refers to itself. However, if that self-reference is evaluated too soon you get null instead of the expected value.

      For example:

      scala> :paste
      // Entering paste mode (ctrl-D to finish)
      
      class Root(child0: => Child) {
       lazy val child = child0
      }
      
      class Child(parent0: => Root) {
       lazy val parent = parent0
      }
      
      // Exiting paste mode, now interpreting.
      
      defined class Root
      defined class Child
      
      scala> val what:Root = new Child(what).parent
      what: Root = null
      
      scala> val w2:Root = new Root(new Child(w2)).child.parent
      w2: Root = null
      

      I would have preferred to get a run time exception or (better) a compile error in this case.


      3.

      For comparison, when used inside {}'s local vals referring to later vals actually are reordered to evaluate properly.
      For example:

      scala> :paste
      // Entering paste mode (ctrl-D to finish)
      
      def ??? = {
      val result = prefix + suffix;
      val prefix = "should this "
      val suffix = "really work?"
      result
      }
      
      // Exiting paste mode, now interpreting.
      
      $qmark$qmark$qmark: java.lang.String
      
      scala> ???
      res4: java.lang.String = should this really work?
      

      This is clever but it does mean that there's an inconsistency between this and a class initialization (where they are not reordered and no error is reported).


      4.

      A val that refers directly to itself either gets zero for itself, or if you put lazy in front you can create infinite recursion:

      scala> val one:Int = 10 - one
      one: Int = 10
      
      scala> lazy val one:Int = 10 - one
      one: Int = <lazy>
      
      scala> one
      java.lang.StackOverflowError
      

      I can only imagine that there is some rare use case for forward and recursive references within vals but I can't help but wonder if this is really a helpful capability or just making things more confusing.

      At my level I can't even figure out how to define a recursive val that isn't infinitely recursive without involving some method that has a side-effect.

      If it were me, I would forbid any reference or use of a val that would ever return any value other than its final, initialized value. This would mean pretty much all the use cases above would return either a compile-time or run-time error where they currently yield a value or zero or null.

        Activity

        Hide
        Paul Phillips added a comment -

        3 is a bug, not cleverness. In trunk it's a compile-time error.

        <console>:9: error: forward reference extends over definition of value result
        val result = prefix + suffix;
        ^

        This ticket is a duplicate of about a thousand others, but I'll leave it open since it gave me an idea. Your advice at the end is impossible unless you want to prohibit overriding vals.

        Here's a val referring to itself and making the fibonacci sequence.

        scala> val fib: Stream[Int] = 1 #:: ((0 #:: fib zip fib) map

        { case (x, y) => x + y }

        )
        fib: Stream[Int] = Stream(1, ?)

        scala> fib take 10 toList
        res0: List[Int] = List(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)

        Show
        Paul Phillips added a comment - 3 is a bug, not cleverness. In trunk it's a compile-time error. <console>:9: error: forward reference extends over definition of value result val result = prefix + suffix; ^ This ticket is a duplicate of about a thousand others, but I'll leave it open since it gave me an idea. Your advice at the end is impossible unless you want to prohibit overriding vals. Here's a val referring to itself and making the fibonacci sequence. scala> val fib: Stream [Int] = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y } ) fib: Stream [Int] = Stream(1, ?) scala> fib take 10 toList res0: List [Int] = List(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)
        Hide
        Adriaan Moors added a comment -

        Paul, assigning to you (in case you want to reopen) and closing (since it seems to me there's nothing more to see here).

        Show
        Adriaan Moors added a comment - Paul, assigning to you (in case you want to reopen) and closing (since it seems to me there's nothing more to see here).

          People

          • Assignee:
            Paul Phillips
            Reporter:
            Dobes Vandermeer
          • Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development