parameter - scala annotations
Resolución implícita inesperada basada en la inferencia del tipo de retorno (1)
Dada una clase de tipo donde la selección de instancia debe realizarse en función del tipo de retorno:
case class Monoid[A](m0: A) // We only care about the zero here
implicit def s[T] : Monoid[Set[T]] = Monoid(Set.empty[T])
implicit def l[T] : Monoid[List[T]] = Monoid(List.empty[T])
def mzero[A](implicit m: Monoid[A]) : A = m.m0
¿Por qué Scala (2.11.6) no resuelve la instancia correcta?
scala> mzero : List[Int]
<console>:24: error: ambiguous implicit values:
both method s of type [T]=> Monoid[Set[T]]
and method l of type [T]=> Monoid[List[T]]
match expected type Monoid[A]
mzero : List[Int]
^
cuando no tiene problemas para encontrar un implícito basado en el tipo de retorno cuando se usa la función implícitamente (lo redefinimos aquí como i para ilustrar cuán similar es a mzero )
def i[A](implicit a : A) : A = a
scala> i : Monoid[List[Int]]
res18: Monoid[List[Int]] = Monoid(List())
El
Monoid[A]
, en lugar del
Monoid[List[Int]]
en el mensaje de error es desconcertante.
Supongo que muchos contribuyentes de scalaz están familiarizados con este problema, ya que parece limitar la conveniencia de las clases de tipos en scala.
EDITAR: Estoy buscando hacer que esto funcione sin renunciar a la inferencia de tipos. De lo contrario, me gustaría entender por qué eso no es posible. Si esta limitación está documentada como un problema de Scala, no podría encontrarla.
1) Después de reescribir su código de la siguiente manera:
case class Monoid[A](m0: A) // We only care about the zero here
implicit def s[T] : Monoid[Set[T]] = Monoid(Set.empty[T])
implicit def l[T] : Monoid[List[T]] = Monoid(List.empty[T])
def mzero[A]()(implicit m: Monoid[A]) : A = m.m0
val zero = mzero[List[Int]]()
val zero2: List[Int] = mzero()
entonces queda claro por qué eso funciona así.
2) Después de tener que establecer mzero como
def mzero[A]()(implicit m: Monoid[_ <: A]) : A = m.m0
entonces habilitó inferencia de tipo adicional para resolver el tipo existencial.
El compilador obtuvo el tipo real del tipo de retorno requerido.
Puede verificarlo con
def mzero[A <: B, B]()(implicit m: Monoid[A]) : A = m.m0
si lo desea.
3) Por supuesto, todo ese comportamiento son solo sutilezas del compilador y no creo que tales casos parciales realmente requieran una comprensión profunda.