Что означает сообщение scalac «расходящееся неявное расширение»?

#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]]]]] область в неявной области, которая сделает его успешным. Вот почему компилятор сообщает, что эта строка исследования была удалена.