¡Has elegido un hueso duro para romper como un principiante de Scala! :-)
Bien, breve recorrido, no espere comprenderlo por completo en este momento. Primero, tenga en cuenta que el problema ocurre en el método ++
. Buscando su definición, la encontramos en el rasgo MapLike
, que recibe un Iterator
o un Traversable
. Como y
es SortedMap
, entonces es la versión Traversable
que se está utilizando.
Tenga en cuenta en su firma de tipo extenso que hay un CanBuildFrom
pasado. Se está aprobando implícitamente, por lo que normalmente no debe preocuparse por ello. Sin embargo, para entender lo que está pasando, esta vez lo haces.
Puede localizar CanBuildFrom haciendo clic en él donde aparece en la definición de ++
, o filtrando. Como menciona Randall en los comentarios, hay un campo en blanco sin marcar en la esquina superior izquierda de la página scaladoc. Solo tiene que hacer clic allí y escribir, y devolverá las coincidencias para lo que sea que haya escrito.
Por lo tanto, busque el rasgo CanBuildFrom
en ScalaDoc y selecciónelo. Tiene una gran cantidad de subclases, cada una responsable de construir un tipo específico de colección. Busque y haga clic en la subclase SortedMapCanBuildFrom
. Esta es la clase del objeto que necesita para generar un SortedMap
de Traversable
. Tenga en cuenta que el constructor de instancias (el constructor de la clase) recibe un parámetro implícito Ordering
. Ahora nos estamos acercando.
Esta vez, utilice el filtro de filtro para buscar Ordering
. Su objeto complementario (haga clic en la pequeña "o" del nombre) aloja un implícito que generará Ordering
s, ya que los objetos complementarios se examinan en busca de implícitos que generen instancias o conversiones para esa clase. Se define dentro del rasgo LowPriorityOrderingImplicits
, que se extiende al objeto Ordering
, y mirándolo verá el método ordered[A <: Ordered[A]]
, que producirá el Ordering
requerido ... o lo produciría, si no hubiera ningún problema.
Uno podría suponer que la conversión implícita de X
a Ordered[X]
sería suficiente, tal como lo había hecho antes, al analizar esto con más detenimiento. Eso, sin embargo, es una conversión de objetos, y ordered
espera recibir un tipo que es un subtipo de Ordered[X]
. Si bien uno puede convertir un objeto de tipo X
en un objeto de tipo Ordered[X]
, X
, no es un subtipo de Ordered[X]
, por lo que no se puede pasar como un parámetro a ordered
.
Por otro lado, se puede crear una implícita val
Ordering[X]
, en lugar de la def
Ordered[X]
, y obtendrá alrededor del problema. Específicamente:
object ViewBoundExample {
class X
def combine[Y](a: SortedMap[X, Y], b: SortedMap[X, Y]): SortedMap[X, Y] = {
a ++ b
}
implicit val orderingX = new Ordering[X] { def compare(x: X, y: X) = 0 }
}
creo que la mayoría de la gente reacción inicial a Ordered
/Ordering
debe ser uno de perplejidad: ¿por qué tiene clases para la misma cosa? El primero se extiende java.lang.Comparable
, mientras que el último se extiende java.util.Comparator
. Por desgracia, el tipo de firma para compare
resume bastante bien la principal diferencia:
def compare(that: A): Int // Ordered
def compare(x: T, y: T): Int // Ordering
El uso de un Ordered[A]
requiere, ya sea para A
que se extienden Ordered[A]
, lo que requeriría que uno sea capaz de modificarA
's definición, o para pasar un método que puede convertir un A
en un Ordered[A]
. Scala es perfectamente capaz de hacer esto último fácilmente, pero entonces usted tiene para convertir cada instancia antes de comparar.
Por otro lado, el uso de Ordering[A]
requiere la creación de un único objeto, como se demostró anteriormente. Cuando lo usa, simplemente pasa dos objetos de tipo A
a compare
- no se crean objetos en el proceso.
Así que hay algunas mejoras en el rendimiento, pero hay una razón mucho más importante para la preferencia de Scala para Ordering
sobre Ordered
. Mire nuevamente en el objeto complementario al Ordering
. Notará que hay varias implicaciones para muchas de las clases de Scala definidas allí. Puede recordar que mencioné anteriormente que se buscará un implícito para la clase T
dentro del objeto complementario de T
, y eso es exactamente lo que está sucediendo.
Este podría hacerse para Ordered
también. Sin embargo, y este es el punto de fricción, eso significa que todos los métodos compatibles con Ordering
y Ordered
¡fallarían! Esto se debe a que Scala buscará un implícito para que funcione y encontrará dos: uno para Ordering
, y otro para Ordered
. Al no poder decidir qué es lo que quería, Scala se da por vencido con un mensaje de error. Por lo tanto, se tuvo que hacer una elección, y Ordering
tuvo más cosas para eso.
Duh, olvidé explicar por qué la firma no está definida como ordered[A <% Ordered[A]]
, en lugar de ordered[A <: Ordered[A]]
. Sospecho que hacerlo causaría la falla de las dobles implícitas que he mencionado antes, pero le preguntaré al tipo que realmente hizo estas cosas y tuvo el doble de problemas implícitos si este método en particular es problemático.
¿Se suponía que 'combinar 'era un método de' X'? –
No, este código era solo una versión mínima de algo en lo que estaba trabajando cuando me encontré con este problema. En el original, X es un tipo de otra biblioteca (java). Combine era un método de utilidad privado en una nueva clase (scala) en la que estaba trabajando; no pretende ser un código plausible, solo reproduce la salida del compilador. –