#scala
#scala
Вопрос:
Я создал небольшой пример программы, чтобы попытаться выяснить, почему более крупная программа не компилировалась.
val o1: Ordered[Int] = 1
val o2: Ordered[Int] = 2
println(o1 < o2)
Когда я передаю это в scala, я получаю:
Ordered.scala:3: error: diverging implicit expansion for type scala.math.Ordering[Ordered[Int]]
starting with method ordered in trait LowPriorityOrderingImplicits
println(o1 < o2)
^
one error found
Использование «-explaintypes» ничего не дает. Однако «-Xlog-implicits» дает следующее:
math.this.Ordering.comparatorToOrdering is not a valid implicit value for scala.math.Ordering[Ordered[Int]] because:
could not find implicit value for parameter cmp: java.util.Comparator[Ordered[Int]]
scala.this.Predef.conforms is not a valid implicit value for Ordered[Int] => java.lang.Comparable[Ordered[Int]] because:
type mismatch;
found : <:<[Ordered[Int],Ordered[Int]]
required: Ordered[Int] => java.lang.Comparable[Ordered[Int]]
/Users/steshaw/Projects/playground/scala/programming-in-scala/Ordered.scala:3: error: diverging implicit expansion for type scala.math.Ordering[Ordered[Int]]
starting with method ordered in trait LowPriorityOrderingImplicits
println(o1 < o2)
^
math.this.Ordering.comparatorToOrdering is not a valid implicit value for scala.math.Ordering[Ordered[Int]] because:
could not find implicit value for parameter cmp: java.util.Comparator[Ordered[Int]]
scala.this.Predef.conforms is not a valid implicit value for Ordered[Int] => java.lang.Comparable[Ordered[Int]] because:
type mismatch;
found : <:<[Ordered[Int],Ordered[Int]]
required: Ordered[Int] => java.lang.Comparable[Ordered[Int]]
one error found
но это мне не помогает. Интересно, что означает это сообщение и как его разрешить?
[Обновление] Тот же код сегодня с Scala 2.11.0 выдает второе сообщение об ошибке в дополнение к первому о «расходящемся неявном расширении». Это второе сообщение весьма полезно.
$ scala Ordered.scala
Ordered.scala:3: error: diverging implicit expansion for type scala.math.Ordering[Ordered[Int]]
starting with method comparatorToOrdering in trait LowPriorityOrderingImplicits
println(o1 < o2)
^
/Users/steshaw/Projects/playground/scala/scalac-errors/Ordered.scala:3: error: type mismatch;
found : Ordered[Int]
required: Int
println(o1 < o2)
^
two errors found
Ответ №1:
Коротко: ваше сообщение об ошибке должно быть просто
error: type mismatch
found: Ordering[Int]
required: Int.
Причина просто в том, что in Ordered[A]
, сравнение выполняется с A
, а не с другими упорядочениями
def <(that: A): Boolean
Это должно быть o1 < 2
, а не o1 < o2
. (конечно 1 < 2
, тоже работает, но я ожидаю, что ваш код — это просто упрощенная версия чего-то другого)
Однако, прежде чем компилятор сообщит об этой простой ошибке, if должен выполнить поиск, может ли какой-либо неявный в области видимости устранить проблему. Например, он может преобразовать Ordering[Int]
o2
в Int
(не может) или Ordering[Int]
o1
во что-то, у чего есть def <(Ordered[Int])
метод Ordered[Ordered[Int]]
. И бывает, что он должен остановить поиск, потому что кажется, что он может продолжаться бесконечно в виде цикла. Правило приведено в спецификации, стр. 107-109 (спецификация для версии 2.9). Однако правило остановки поиска является пессимистичным, и возможно, что оно отбрасывает строку поиска, которая могла быть успешной, поэтому компилятор считает, что он должен сообщить об этом. Хотя на самом деле, большую часть времени, как и здесь, цикл был должным образом отброшен, и никакого решения не существовало. Это то, что приводит к неожиданному сообщению об ошибке. Я думаю, что о более простой ошибке тоже следует сообщать, и более заметно.
Позвольте мне дать некоторые ограниченные объяснения того, почему в неявном поиске может быть цикл. Может быть
implicit def f(implicit a: A): B
это означает, что если A
у вас есть неявное, B
у вас тоже есть неявное. Таким образом, получается график между типами: A
обеспечивает B
. Это сложнее, чем это, на самом деле это гиперграф : implcit def f(implicit a: A, implicit b: B): C
: A
и B
обеспечивает C
.
С дженериками у вас есть бесконечное количество типов и бесконечный (гипер) граф, который становится еще более сложным из-за подтипов (если вам нужен A
, подойдет любой подтип A
. Добавьте правило подтипирования, подразумеваемое ковариацией / контравариантностью)
График может содержать цикл: чтобы получить an A
, вы можете просто указать a B
; чтобы получить a B
, вы можете просто указать a C
; чтобы получить a C
, вы можете просто указать an A
. Это означает, что если вы предоставляете an A
, вы получаете an A
, что бесполезно, и эта строка поиска должна быть удалена. В данном случае это не проблема: это фактический цикл, и нет риска обойти возможное решение, отбросив его.
Но это может быть более сложным. Поскольку граф бесконечен, поиск может быть бесконечным без точного циклирования. Если у вас есть
implicit def[A](x: X[X[A]]): X[A]
тогда, если вы ищете X[Int]
, вы можете искать X[X[Int]]
вместо этого, но затем, с тем же правилом, вы ищете X[X[X[Int]]]
и так далее. Это не совсем цикл, но компилятор не отслеживает эти строки и называет его расходящимся. За исключением того, что где-то может быть неявная X[X[X...X[Int]]]]]
область в неявной области, которая сделает его успешным. Вот почему компилятор сообщает, что эта строка исследования была удалена.