Unexpected implicit resolution based on inference from return type

ScalaType InferenceImplicitReturn TypeScalaz

Scala Problem Overview


Given a typeclass where instance selection should be performed based on the return type:

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

why does Scala (2.11.6) fail to resolve the proper instance:

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

when it has no problems finding an implicit based on the return type when using the implicitly function (we redefine it here as i to illustrate how similar it is to mzero)

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

The Monoid[A], instead of Monoid[List[Int]] in the error message is puzzling.

I would assume many scalaz contributors to be familiar with this problem as it seems to limit the convenience of typeclasses in scala.

EDIT: I'm looking into getting this working without forgoing type inference. Otherwise I'd like understand why that's not possible. If this limitation is documented as a Scala issue, I could not find it.

Scala Solutions


Solution 1 - Scala

  1. After rewriting your code as follows:

    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 mzeroA(implicit m: Monoid[A]) : A = m.m0

    val zero = mzeroList[Int] val zero2: List[Int] = mzero()

then becomes clearly why that works so.

  1. After you had to set mzero as def mzero[A]()(implicit m: Monoid[_ <: A]) : A = m.m0 then you enabled additional type inference to resolve existencial type. Compiler got actual type from required return type. You could check it with def mzero[A <: B, B]()(implicit m: Monoid[A]) : A = m.m0 if you want.

  2. Of course all that behaviour is just subtleties of compiler and I not think that such partial cases really required deep understanding.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPatrick Pr&#233;montView Question on Stackoverflow
Solution 1 - Scalauser1303559View Answer on Stackoverflow