【问题标题】:Scala bug? DelayedInit and Implicits斯卡拉错误? DelayedInit 和隐式
【发布时间】:2012-03-09 13:14:01
【问题描述】:

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

我创建了一个独立的示例:

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

【问题讨论】:

  • 我觉得很困惑,你在 def print(第 7 行)中重用 T 作为标识符,这导致了一个问题,implicitly [Printer [T]] 是隐含的打印机打印机吗?在这里使用不同的标识符是否有助于避免混淆?
  • @userunknown 我不太清楚你的意思,或者更简单的写法?
  • 您能更具体地谈谈您的问题吗?你说“这可以编译,但它不应该”之类的话,但这是一个陈述,而不是一个问题。你的问题是什么

标签: scala


【解决方案1】:

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

def delayedInit(x: => Unit) {  }

然后在你的主要方法中做一些类似

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 的存在无关。类/对象初始化器非常乐意使用引用未定义 vals 的 val 初始化器进行编译。

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

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

您的问题似乎可以归结为“从DelayedInit 派生的类/对象的主体是否应该像类主体或功能块一样编译?”

看起来 Odersky 选择了前者,但您希望选择后者。

【讨论】:

    【解决方案2】:

    这和我写这个是一样的错误:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-17
      • 2017-08-22
      • 1970-01-01
      • 2017-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多