Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A simpler way to returning the "current" type in Scala.
/** 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))
}
@nuttycom

This comment has been minimized.

Copy link

@nuttycom nuttycom commented May 6, 2015

The problem with this solution is that the following would be a valid Pet:

case class CatFish(name: String, age: Int) extends Pet {
  type This = Kitty
  def renamed(newName: String): Kitty = Kitty(newName, age)
}

In short, it has the same safety failure as the F-bounded type solution.

@rjean-gilles

This comment has been minimized.

Copy link

@rjean-gilles rjean-gilles commented May 7, 2015

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.

@greenrd

This comment has been minimized.

Copy link

@greenrd greenrd commented Jun 24, 2015

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".

@immediatus

This comment has been minimized.

Copy link

@immediatus immediatus commented Nov 12, 2015

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;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment