I have the following code defining a type class.
trait Foo[T] {
def toFoo(x: T): String
}
trait Foos {
def toFoo[T](f: T => String): Foo[T] = new Foo[T] {
def toFoo(x: T): String = f(x)
}
}
object Foo extends Foos {
def toFoo[A: Foo](a: A) = implicitly[Foo[A]].toFoo(a)
implicit def AToFoo: Foo[A] = toFoo { c =>
"A"
}
implicit def BToFoo[T]: Foo[B] = toFoo { c =>
"B"
}
implicit def ListToFoo[T: Foo]: Foo[List[T]] = toFoo { c =>
c.map(toFoo(_)).
}
}
class A
class B extends A
Now if I have if I do toFoo(List(new A, new B) I get List("A", "A") instead of List("A", "B"). How can I ensure that the BtoFoo method is used rather than AToFoo for classes with type B?
implicit resolution is a purely compile-time mechanism. The easy way to distinguish between subtypes here is to match inside the implicit. Remove
BToFooaltogether and have instead theAversion deal with both casesOf course, it is also possible to delegate to a method for part of the hierarchy. You may delegate to a method in
A, overidde inB, and still have the typeclass work for List, where you cannot add a method.You may also consider declaring
Foocontravariant.