Reputation: 13922
Suppose I have two traits:
trait Generic1[T] {
def mapR[U](f: Result[T] => Result[U]): Generic1[U]
}
trait Generic2[A, T] {
// pretty much the same as Generic1, but with the extra `A` type param
def mapR[U](f: Result[T] => Result[U]): Generic2[A, U]
}
Is there a way that I can define a single trait that defines some common functionality based on the mapR
method?
// for example
trait MapR[T, ???] {
def mapR[U](f: Result[T] => Result[U]): ???[U]
def map[U](f: T => U): ???[U] = mapR(_ map f)
def flatMap[U](f: T => Result[U]): ???[U] = mapR(_ flatMap f)
def withFilter(f: T => Boolean): ???[T] = mapR(_ withFilter f)
}
How would the ???
be defined in the MapR
trait above so that I could instead define
trait Generic1[T] extends MapR[T, ???]
trait Generic2[A, T] extends MapR[T, ???]
extra issues
@m-z's answer handles the question asked above, but I run into some further troubles when the GenericN traits have type variance:
trait Generic1[+T] extends MapR[T] {
type R[X] = Generic1[X]
...
}
trait Generic2[-A, +T] extends MapR[T] {
type R[X] = Generic2[A, X]
...
}
I get errors like "contravariant type A occurs in invariant position in type [X]Generic2[A, X] of type R"
I tried redefining R to be covariant, but it still doesn't touch the A
type and I get similar errors.
Can the approach in the answer be adapted to handle the type variance described above?
Upvotes: 0
Views: 71
Reputation: 55569
You can make the MapR
trait have a type member with a single type parameter than can alias the Generic
traits on implementation. This would require each Generic
trait to describe how it is parameterized.
trait Result[T]
trait MapR[T] {
type R[U]
def mapR[U](f: Result[T] => Result[U]): R[U]
}
trait Generic1[T] extends MapR[T] {
type R[U] = Generic1[U]
}
trait Generic2[A, T] extends MapR[T] {
type R[U] = Generic2[A, U]
}
Upvotes: 2
Reputation: 1575
You could probably achieve this by declaring the supertrait as higher-kinded. Something along these lines might be possible:
trait Result[T]
trait MapRSupport[SelfType[X], T] {
def mapR[U](f: Result[T] => Result[U]): SelfType[U]
}
trait Generic1[T] extends MapRSupport[Generic1, T]{
def mapR[U](f: Result[T] => Result[U]): Generic1[U] = ???
}
trait Generic2[A] extends MapRSupport[Generic2, A]{
type T
// pretty much the same as Generic1, but with the extra `A` type param
def mapR[U](f: Result[T] => Result[U]): Generic2[A] { type T = U} = ???
}
Upvotes: 1