Reputation: 410592
Say I have a trait like this:
trait BaseTrait[A] {
def someMethod[B](f: A => T[B]): T[B]
}
I want T
to basically be "the same type as the class that implements this method". In other words, I want to be able to write this:
class ConcreteClass[A] extends BaseTrait[A] {
def someMethod[B](f: A => ConcreteClass[B]): ConcreteClass[B]
}
And have that method satisfy the trait's requirement for someMethod
. Is that possible?
Upvotes: 1
Views: 523
Reputation: 446
What you are asking about is called F-bound types, and this is the best explanation I've seen on the topic: https://tpolecat.github.io/2015/04/29/f-bounds.html
The punch line is that you'll either want to switch to using typeclasses rather than inheritance, or you'll have to settle for getting almost all the way to what you want, like this:
trait BaseTrait[A <: BaseTrait[A]] { this: A =>
def someMethod[B](f: A => T[B]): T[B]
}
class Concrete extends BaseTrait[Concrete]{
def someMethod[B](f: A => T[B]): T[B]
}
Now in your case your trait itself takes a type parameter, so you'll also have to work in an other type parameter, and use higher-kinded types like so:
trait BaseTrait[A, T[X] <: BaseTrait[X, T]] { this: T[A] =>
def someMethod[B](f: A => T[B]): T[B]
}
class Concrete[A] extends BaseTrait[A, Concrete]{
def someMethod[B](f: A => Concrete[B]): Concrete[B] = ???
}
//class DoesNotCompile[A] extends BaseTrait[Concrete, A]{
// def someMethod[B](f: A => Concrete[B]): Concrete[B] = ???
//}
//class DoesNotCompile2[A] extends BaseTrait[DoesNotCompile2, A]{
// def someMethod[B](f: A => Concrete[B]): Concrete[B] = ???
//}
object Ex {
val concrete: Concrete[List[Int]] = new Concrete[List[Int]]
val concrete2: Concrete[String] = concrete.someMethod(_ => new Concrete[String])
}
Upvotes: 8
Reputation: 18424
Here's a version using a type member instead of a type parameter. I think it's down to personal preference which you prefer.
trait BaseTrait[A] {
type Self[X] <: Base[X]
def doSomething[B](f: A => Self[B]): Self[B]
}
class ConcreteClass[A] extends BaseTrait[A] {
type Self[X] = ConcreteClass[X]
def doSomething[B](f: A => ConcreteClass[B]): ConcreteClass[B] = ???
}
It wouldn't stop some concrete implementation from defining Self
to be something other than itself, but I don't know of any solution to that (it's a common problem).
Upvotes: 1
Reputation: 27535
Maybe something like?:
trait BaseTrait[M[_], A] { self: M[A] =>
def someMethod[B](f: A => M[B]): M[B]
}
class ConcreteClass[A] extends BaseTrait[ConcreteClass, A] {
def someMethod[B](f: A => ConcreteClass[B]): ConcreteClass[B] = ???
}
I think it would be difficult to get it more concrete than that.
Upvotes: 3