You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
#4856
Closed
scabug opened this issue
Jul 31, 2011
· 3 comments
The behavior or forward references to vals and recursive values is a bit odd and inconsistent.
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)classFoo {
valbar= x+1valx=5
}
// Exiting paste mode, now interpreting.
defined classFoo
scala>valfoo=newFoo
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.
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)classRoot(child0: =>Child) {
lazyvalchild= child0
}
classChild(parent0: =>Root) {
lazyvalparent= parent0
}
// Exiting paste mode, now interpreting.
defined classRoot
defined classChild
scala>valwhat:Root=newChild(what).parent
what:Root=null
scala>valw2:Root=newRoot(newChild(w2)).child.parent
w2:Root=null
I would have preferred to get a run time exception or (better) a compile error in this case.
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???= {
valresult= prefix + suffix;
valprefix="should this "valsuffix="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).
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>valone:Int=10- one
one:Int=10
scala>lazyvalone: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.
The text was updated successfully, but these errors were encountered:
@paulp said:
3 is a bug, not cleverness. In trunk it's a compile-time error.
: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, ?)
The behavior or forward references to vals and recursive values is a bit odd and inconsistent.
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:
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.
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:
I would have preferred to get a run time exception or (better) a compile error in this case.
For comparison, when used inside {}'s local vals referring to later vals actually are reordered to evaluate properly.
For example:
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).
A val that refers directly to itself either gets zero for itself, or if you put lazy in front you can create infinite recursion:
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.
The text was updated successfully, but these errors were encountered: