Неожиданное неявное разрешение на основе вывода из возвращаемого типа

Для класса типов, где выбор экземпляра должен выполняться на основе возвращаемого типа:

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

почему Scala (2.11.6) не может решить соответствующий экземпляр:

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]
              ^

когда у него нет проблем с поиском неявного на основе типа возврата при использовании функции неявно (мы переопределяем его здесь как i, чтобы показать, насколько это похоже на mzero)

def i[A](implicit a : A) : A = a
scala> i : Monoid[List[Int]]
res18: Monoid[List[Int]] = Monoid(List())

Monoid[A], а не Monoid[List[Int]] в сообщении об ошибке вызывает недоумение.

Я бы предположил, что многие разработчики сказанов знакомы с этой проблемой, поскольку это, по-видимому, ограничивает удобство классных классов в scala.

EDIT: Я ищу, чтобы это работало без отклонения от типа. В противном случае я бы хотел понять, почему это невозможно. Если это ограничение документировано как проблема Scala, я не смог его найти.

Ответ 1

1) После перезаписи кода выполните следующие действия:

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()

тогда становится ясно, почему это работает.

2) После того, как вам нужно было установить mzero как def mzero[A]()(implicit m: Monoid[_ <: A]) : A = m.m0, вы включили дополнительный тип вывода для разрешения существующего типа. Компилятор получил фактический тип из требуемого типа возврата. Вы можете проверить его с помощью def mzero[A <: B, B]()(implicit m: Monoid[A]) : A = m.m0, если хотите.

3) Конечно, все это поведение - это просто тонкости компилятора, и я не думаю, что такие частичные случаи действительно требовали глубокого понимания.