Last active
April 5, 2024 13:34
-
-
Save odersky/56323c309a186cffe9af to your computer and use it in GitHub Desktop.
A simpler way to returning the "current" type in Scala.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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)) | |
} |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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".