Malte Schwerhoff
Malte Schwerhoff

Reputation: 12852

Abstract classes/traits and invariant functions

Given a trait T

trait T {
  def v: Int
  def +(t: T): T
}

the following class A

case class A(v: Int) extends T {
  def +(a: A) = A(v + a.v)
}

is not a valid subtype of T. The implementation of A.+ is too restrictive, because it only accepts elements of type A whereas the signature of T.+ requires all implementations to be able to accept objects of type T and not just objects of type A. So far, so reasonable.

If I'd like to allow implementations of T to be that restrictive I can modify the declarations of T and A as follows

trait T[This <: T[This]] {
  def v: Int
  def +(t: This): This
}

case class A(v: Int) extends T[A] {
  def +(a: A) = A(v + a.v)
}

which obviously blows up the type signature.

Is there another way to declare that implementations of T only need to be compatible with objects of their own type?

1st EDIT In reply to Landei's answer below:

While self-types indeed shorten the current signature they don't shorten other signatures where T occurs, e.g.

trait C[D <: T[D], S] { self: S =>
  def +(perm: D): S
  def matches(other: S): Boolean
}

Upvotes: 2

Views: 197

Answers (2)

psp
psp

Reputation: 12158

You can do it with type members. I don't know what brand of "short" you're after here exactly. There is some redundancy, but on the other hand no type parameters passes big bracket savings on to you.

trait TT {
  type This <: TT
  def v: Int
  def +(t: This): This
}
case class AA(v: Int) extends TT {
  type This = AA
  def +(a: This) = AA(v + a.v)
}

Upvotes: 1

Landei
Landei

Reputation: 54574

You can use self-types:

trait T[S] {
  self:S => 
  def v: Int
  def +(t: S): S
}

case class A(v: Int) extends T[A] {
  def +(a: A) = A(v + a.v)
}

Upvotes: 1

Related Questions