Scala bug? DelayedInit和Implicits

时间:2012-03-09 01:47:22

标签: scala

我发现非常奇怪的行为(scala 2.9.1)使用和定义隐式值,并想知道是否有人可以解释它,或者它是否是scala bug?

我创建了一个自包含的示例:

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 
  }
}

2 个答案:

答案 0 :(得分:2)

假设您将delayInit的定义更改为无操作,即

def delayedInit(x: => Unit) {  }

然后在你的main方法中执行类似

的操作
println("FP.imp: " + FalsePositive.imp)

正如预期的那样,将打印FP.imp: null,但练习的真正要点是说明定义FalsePositive的主体的块的作用类似于常规类主体,而不是函数主体。它在看到val时定义公共成员,而不是局部变量。

如果您将方法添加到AnnoyingObjectForNoPurpose,如下所示,则无法编译,因为print的隐含要求不满足。

def fails {
  print(321)
  implicit val cantSeeIt = new Printer[Int] {
    def doPrint(v: Int) = println(v + " doesn't compile")
  }
}

但是,如果您按照相同的原则定义了一个类,它将进行编译,但在初始化时会在运行时失败,就像您的FalsePositive示例一样。

class Fine {
  print(321)
  implicit val willBeNull = new Printer[Int] {
    def doPrint(v: Int) = println(v + " compiles, but fails")
  }
}

要明确的是,Fine的编译行为与implicit的存在无关。类/对象初始化程序非常乐意使用引用未定义val s的val初始化程序进行编译。

object Boring {
  val b = a
  val a = 1
  println("a=%s b=%s".format(a, b))
}

Boring编译得很好,当它被引用时,会打印a=1 b=0

似乎你的问题可以归结为“是否应该将派生自DelayedInit的类/对象的主体编译成类主体或功能块?”

看起来奥德斯基选择了前者,但你希望后者。

答案 1 :(得分:1)

这就像我写这个错误一样:

object Foo {
   println(x)
   val x = 5
}

这不是一个错误。你的构造函数向下对象体并按顺序发生。 DelayedInit不是原因。这就是为什么在使用val / vars时要小心并确保它们首先初始化的原因。这也是人们使用lazy val来解决初始化顺序问题的原因。