-
-
Save odersky/56323c309a186cffe9af to your computer and use it in GitHub Desktop.
/** This is in reference to @tploecat's blog http://tpolecat.github.io/2015/04/29/f-bounds.html | |
* where he compares F-bounded polymorphism and type classes for implementing "MyType". | |
* | |
* Curiously, the in my mind obvious solution is missing: Use abstract types. | |
* | |
* A lot of this material, including an argument against F-bounded for the use-case | |
* is discussed in: | |
* | |
* Kim B. Bruce, Martin Odersky, Philip Wadler: | |
* A Statically Safe Alternative to Virtual Types. ECOOP 1998: 523-549 | |
*/ | |
trait Pet { | |
type This <: Pet | |
def name: String | |
def renamed(newName: String): This | |
} | |
case class Fish(name: String, age: Int) extends Pet { | |
type This = Fish | |
def renamed(newName: String): Fish = copy(name = newName) | |
} | |
case class Kitty(name: String, age: Int) extends Pet { | |
type This = Kitty | |
def renamed(newName: String): Kitty = copy(name = newName) | |
} | |
object Test { | |
def esquire[A <: Pet](a: A): a.This = a.renamed(a.name + ", Esq.") | |
val f: Fish = esquire(new Fish("bob", 22)) | |
} |
This seems easily fixed by changing the definition of This
:
type This >: this.type <: Pet
I may have overlooked some other limitation , but at first sight ti seems to do the job just fine.
Even with rjean-gilles' addition, you probably still want F-bounded polymorphism for type preservation (which is quite useful in immutable programming). Without it, This
can vary between those two bounds - you want it to be the same as the target type of your method call when you call a method, if you want the method to return a value "of the same type".
What about have more than one level of inheritance?
Something like:
class Kitty(val name: String, val age: Int) extends Pet {
type This = Kitty
def renamed(newName: String): Kitty = new Kitty(newName, age)
}
class Cat(name: String) extends Kitty(name, 2) {
override type This = Cat
override def renamed(newName: String): Cat = new Cat(newName)
}
error: overriding type This in class Kitty, which equals Kitty;
What about have more than one level of inheritance?
Something like:
class Kitty(val name: String, val age: Int) extends Pet { type This = Kitty def renamed(newName: String): Kitty = new Kitty(newName, age) } class Cat(name: String) extends Kitty(name, 2) { override type This = Cat override def renamed(newName: String): Cat = new Cat(newName) }
error: overriding type This in class Kitty, which equals Kitty;
I also want to know!
Not the best, but you can override variables and methods with more specific types. Works for my situation:
trait Container {
type T = Any
val contents: Any
}
class Animal(val name: String)
class Fish(name: String) extends Animal(name)
class A extends Container {
override val contents: Animal = new Animal("Animal")
}
class AA extends A {
override val contents: Fish = new Fish("Fish")
}
class B extends Container {
override val contents: Int = 3874
}
val a = new A
println(a.contents.name) // Animal
val b = new B
println(b.contents) // 3874
val aa = new AA
println(aa.contents.name) // Fish
The problem with this solution is that the following would be a valid Pet:
In short, it has the same safety failure as the F-bounded type solution.