I've found extremely weird behaviour (scala 2.9.1 ) using and defining implicit values, and wondering if anyone can explain it, or if it's a scala bug?
I've created a self contained example:
object AnnoyingObjectForNoPurpose {
trait Printer[T] {
def doPrint(v: T): Unit
}
def print[T : Printer](v: T) = implicitly[Printer[T]].doPrint(v)
trait DelayedRunner extends DelayedInit {
def delayedInit(x: => Unit){ x }
}
// this works, as it should
object Normal extends DelayedRunner {
implicit val imp = new Printer[Int] {
def doPrint(v: Int) = println(v + " should work")
}
print(343)
}
// this compiles, but it shouldn't
// and won't run, cause the implicit is still null
object FalsePositive extends DelayedRunner {
print(123)
implicit val imp = new Printer[Int] {
def doPrint(v: Int) = println(v + " should not compile")
}
}
def main(args: Array[String]) {
implicit val imp = new Printer[Int] {
def doPrint(v: Int) = println(v + " should work")
}
print(44)
// print(33.0) // correctly doesn't work
Normal // force it to run
FalsePositive // force this to run too
}
}
Suppose you changed your definition of delayInit
to be a no-op, ie
def delayedInit(x: => Unit) { }
Then in your main method do something like
println("FP.imp: " + FalsePositive.imp)
As expected that will print FP.imp: null
, but the real point of the exercise is to illustrate that the block that defines the body of FalsePositive
is acting like a regular class body, not a function body. It's defining public members when it sees val
, not local variables.
If you added a method to AnnoyingObjectForNoPurpose
like the following, it wouldn't compile because print
's implicit requirement isn't satisfied.
def fails {
print(321)
implicit val cantSeeIt = new Printer[Int] {
def doPrint(v: Int) = println(v + " doesn't compile")
}
}
However if you defined a class along the same principle, it would compile, but fail at runtime when initialized, just like your FalsePositive
example.
class Fine {
print(321)
implicit val willBeNull = new Printer[Int] {
def doPrint(v: Int) = println(v + " compiles, but fails")
}
}
To be clear, the compile behavior of Fine
has nothing to do with the presence of the implicit
. Class/object initializers are very happy to compile with val
initializers which reference undefined val
s.
object Boring {
val b = a
val a = 1
println("a=%s b=%s".format(a, b))
}
Boring
compiles just fine and when it is referenced, it prints a=1 b=0
It seems like your question boils down to "Should the body of a class/object deriving from DelayedInit
be compiled as if it's a class body or a function block?"
It looks like Odersky picked the former, but you're hoping for the latter.
That's the same bug as if I write this:
object Foo {
println(x)
val x = 5
}
It ain't a bug. Your constructor flows down the object body and happens in order. DelayedInit
isn't the cause. This is why you need to be careful when using val/vars and ensure they init first. This is also why people use lazy val's to resolve initialization order issues.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.