Почему в Scala можно назначать рекурсивные лямбда-выражения неленивым val?

В следующем операторе val f определяется как лямбда, которая ссылается сама на себя (это рекурсия):

val f: Int => Int = (a: Int) =>
    if (a > 10) 3 else f(a + 1) + 1 // just some simple function

Я пробовал его в REPL, и он правильно компилируется и выполняется.

Согласно спецификации, это похоже на незаконную прямую ссылку:

В последовательности операторов s[1]...s[n], составляющих блок, если простое имя в s[i] относится к объекту, определенному s[j], где j >= i, то для всех s[k] между s[i] и s[j] включительно,

  • s[k] не может быть определением переменной.
  • Если s[k] является определением значения, оно должно быть lazy.

Присваивание представляет собой один оператор, поэтому оно удовлетворяет критериям j >= i и включено в интервал операторов, к которым применяются два правила (между s[i] и s[j] включительно).

Однако похоже, что это нарушает второе правило, потому что f не ленится.

Как это юридическое утверждение (пробовал в Scala 2.9.2)?


person corazza    schedule 17.07.2014    source источник


Ответы (1)


Вероятно, вы пытались использовать это в REPL, который заключает все содержимое в определение объекта. Это важно, потому что в Scala (или лучше: в JVM) все значения экземпляра инициализируются значением по умолчанию, которое равно null для всех AnyRefs и 0, 0.0 или false для AnyVals. Для значений метода эта инициализация по умолчанию не происходит, поэтому в этом случае вы получаете сообщение об ошибке:

scala> object x { val f: Int => Int = a => if (a > 10) 3 else f(a+1)+1 }
defined object x

scala> def x { val f: Int => Int = a => if (a > 10) 3 else f(a+1)+1 }
<console>:7: error: forward reference extends over definition of value f
       def x { val f: Int => Int = a => if (a > 10) 3 else f(a+1)+1 }
                                                           ^

Такое поведение может даже привести к странным ситуациям, поэтому следует быть осторожным с рекурсивными значениями экземпляра:

scala> val m: Int = m+1
m: Int = 1

scala> val s: String = s+" x"
s: String = null x
person kiritsuku    schedule 17.07.2014
comment
Да, вы правы, это было в REPL. У меня было предчувствие, что это может быть что-то вроде этого, я знал, что за Scala REPL должна стоять какая-то черная магия (в конце концов, Scala — компилируемый язык, поэтому я был немного сбит с толку, когда увидел, что у него даже есть REPL — Я просто предположил, что он собрал все операторы, которые вы вводили ранее, и поместил их в def main или что-то в этом роде). - person corazza; 18.07.2014