Reputation: 333
I have an abstract class that will be implemented in many different ways. I am trying to create a method that can take any one of these implementors as a parameter, and return a new instance of the same type. I am trying to implement this behavior by passing a "factory" function to the method that yields a new object of an abstract type (which will be set appropriately in each implementor).
I've tried to breakdown the problem I'm having into the following code:
My parent abstract class:
abstract class Parent(val x: Int) {
type Self <: Parent
def factory: Int ⇒ Self
def me = "Parent"
}
An example of a Child class:
class Child(x: Int) extends Parent(x) {
type Self = Child
override def factory: Int ⇒ Self = {
(v: Int) ⇒ new Child(v)
}
override def me = "Child"
}
I'm trying to use the Self
type parameter as a way to ensure that the factory
method generates an object of the correct type.
Now the method itself:
object Parent {
def transform[T <: Parent](input: T#Self, factory: (Int ⇒ T#Self)): T#Self = {
//do stuff with input
input.me
val result = 123
factory(result)
}
}
Now when I try to actually wire this all up:
class Transformer[T <: Parent] {
var everyone: List[T#Self] = List.empty
def start() = {
val updated = for (e ← everyone) yield {
Parent.transform[T](e, e.factory)
}
everyone = updated
}
}
I get a compile error when I try to pass the factory to the transform method
Type mismatch, expected (Int) => T#Self, actual (Int) => Parent.this.Self
I've tried a variety of things to get this to work, but no luck. I'm very new to this still, so it's possible (probably likely) I'm trying to do something crazy here. A better alternative would be greatly appreciated, but I'm still interested to see if it's possible to get something like this to work. The end goal is to have a way for the transform
method to generate new instances of the exact same type that was provided as a parameter.
Any help is greatly appreciated. Thanks!
Upvotes: 0
Views: 304
Reputation: 4268
I'm trying to use the Self type parameter as a way to ensure that the factory method generates an object of the correct type.
This reminds me of tpolecat's Returning the "Current" Type in Scala:
I have a type hierarchy … how do I declare a supertype method that returns the “current” type?
We can adapt the F-Bounded Types approach discussed in that post to your Parent
and Child
hierarchy:
trait Parent[A <: Parent[A]] { this: A =>
def x: Int
def factory: Int ⇒ A
def me = "Parent"
}
class Child(override val x: Int) extends Parent[Child] {
override def factory = (v: Int) ⇒ new Child(v)
override def me = "Child"
}
class OtherChild(override val x: Int) extends Parent[OtherChild] {
override def factory = (v: Int) ⇒ new OtherChild(v)
override def me = "OtherChild"
}
object Parent {
def transform[A <: Parent[A]](input: A): A = {
//do stuff with input
input.me
val result = 123
input.factory(result)
}
}
And then, following the Bonus Round: How do we deal with collections?
section, your Transformer
becomes something like this:
class Transformer {
import scala.language.existentials
var everyone = List[A forSome { type A <: Parent[A] }](new Child(1), new OtherChild(2))
def start() = {
val updated = everyone.map(Parent.transform(_))
everyone = updated
}
}
Upvotes: 1