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.
mzero[List[Int]]
.mzero
asdef mzero[A](implicit m: Monoid[_<:A]) : A = m.m0
. Ut I'd personnaly go withmzero[List[Int]]
as Travis Brown mentioned.Monoid
covariant, which indeed makes the code compile. As you probably cannot make it covariant (given that a complete definition ofMonoid
would have occurences ofA
in contravariant position), the next best thing is to try to emulate this onmzero
alone by using an existential type, givingdef mzero[A](implicit m: Monoid[_<:A])
. I would definitely be interested in getting an explanation of this oddness myself.