Reputation: 1706
I currently program some classes which implement type classes. I use cats for this purpose. In this setting, I have classes A[T]
and B[T] <: A[T]
. A[T]
is a monad, so when importing the right libraries, I can write
val x: A[Int] = ???
x.flatMap(_)
but I can't write
val x: B[Int] = ???
b.flatMap(_)
as scala doesn't find the flatMap operator. Instead, I have to write
val x: B[Int] = ???
(b: A[Int]).flatMap(_)
for this to work. This is awkward as I expect subclasses to inherit the methods of superclasses.
For completeness, I add a minimal example without dependencies which shows the issue. In this, Filterable
is a typeclass, that is implemented by S
.
object StackOverflowTest {
class S[+T]
class Q[+T] extends S[T]
trait Filterable[F[+_]] {
def filter[A](x: F[A]): F[A]
}
implicit class FilterOps[F[+_], A](y: F[A])(implicit ev: Filterable[F]) {
def filter: F[A] = ev.filter(y)
}
implicit object SIsFilterable extends Filterable[S] {
def filter[A](x: S[A]): S[A] = x
}
//def failing = {
// val q = new Q[Int]()
// val r = q.filter
//}
def working = {
val q = new Q[Int]()
val r = (q: S[Int]).filter
}
}
The failing
definition doesn't compile, because q.op
isn't found. On the other hand, working
mitigates this issue by first casting to S
. What can I do to make the failing
example work for users of my library? It feels very unnatural that you have to cast to a parent type to be able to use a method of it.
Note, that I can't define a second implicit object which implements Filterable[Q]
because (new Q[Int]()).filter
should be of type S[Int]
and not Q[Int]
.
Upvotes: 1
Views: 98
Reputation: 6182
Try replacing SIsFilterable
with:
implicit def sOrSubtypeIsFilterable [T[+_] <: S[_]] = new Filterable[T] {
def filter[A](x: T[A]): T[A] = x
}
Upvotes: 1