Скала ошибка? DelayedInit и последствия - PullRequest
1 голос
/ 09 марта 2012

Я обнаружил крайне странное поведение (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 
  }
}

Ответы [ 2 ]

2 голосов
/ 09 марта 2012

Предположим, вы изменили определение 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.Инициализаторы класса / объекта очень рады компилировать с val инициализаторами, которые ссылаются на undefined val s.

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

Boring компилируется очень хорошо, и когда на него ссылаются, он печатает a=1 b=0

Похоже, ваш вопрос сводится к "Должно ли тело класса / объекта, происходящего из DelayedInit, быть скомпилировано так, как если бы оно было телом класса или функциональным блоком?"

Похоже на Одерскоговыбрал первое, но вы надеетесь на второе.

1 голос
/ 09 марта 2012

Это такая же ошибка, как если бы я написал это:

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

Это не ошибка. Ваш конструктор передает вниз тело объекта и происходит в порядке. DelayedInit не причина. Вот почему вы должны быть осторожны при использовании val / vars и убедиться, что они инициализируются первыми. Именно поэтому люди используют ленивые значения val для решения проблем порядка инициализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...