Question

Working through these posts had me thinking I understood self-types, at least somewhat.

So I created an example which failed as expected:

scala> trait A { val v = "a" }
defined trait A

scala> trait B { this :A => ; var v = "" ; this.v = "b" }
<console>:6: error: reassignment to val
       trait B { this :A => ; var v = "" ; this.v = "b" }
                                                  ^

The self-type's "this" shadows B's "this" -- it looks weird, but makes sense.

It would seem wise, then, to give the self-type a different name. I did this and was rather surprised:

scala> trait C { a :A => ; var v = "" ; this.v = "c" }
<console>:6: error: reassignment to val
       trait C { a :A => ; var v = "" ; this.v = "c" }
                                               ^

It's still shadowed???

Changing the name of the 'local' variable let things compile, which makes sense:

scala> trait D { a :A => ; var w = "" ; this.w = a.v }
defined trait D

(And the self-type name can optionally be used to clarify which "v" to use.)

Okay. Which means that the following should fail?

scala> trait E { this :A => ; var w = "" ; this.w = this.v }
defined trait E

Huh? Which this is this? :-(

So... is there a point to naming self-types? "this" seems to end up shadowed regardless.

Or is this an edge-case of scoping rules, where the self-type's "this" takes precedence over the trait's "this" -- and one should just avoid using the same names for things in related traits?

Was it helpful?

Solution

Your problem is not the name of the self type (in all your examples both this and the alternate self-type name refer to the very same thing and have the same type, namely ‘the greatest lower bound of B and A’ [§5.1, Scala Language Spec]) but that you try to define a field with the same name again without explicitly overriding it.

Look at the simpler example:

trait A { val v = "a" }
trait B { this: A =>
  var v = "b"
}

new A with B {} // does not compile

<console>:9: error: overriding value v in trait A$class of type java.lang.String;
 variable v in trait B$class of type java.lang.String needs `override' modifier
   new A with B {}

So, even though, you don’t get an error in defining B, you’re simply not able to use it anyway.

This would work

trait A { val v = "a" }
trait B { this:A => override val v = "b"  }

new A with B {}

Here, you are explicitly overriding A’s v in B. (Note that new B with A {} would fail because B needs to come last.) Also, it has to be a val because in most cases you cannot really override vars and you cannot override something else using a var.

Generally, you should not be concerned about the name of the self-type in these simple cases. As long as you do not create another trait or class inside B, both this and whatever you’d call your self-type variable will point to the same thing. There’ll be no shadowing. If you had a new trait inside B, and you needed to refer to the instance of B inside that trait, you’d need another name for your self-type.

Consider this

trait B { this =>
  val v = "b"
  trait C {
    val v = "c"
    println(this.v)
  }
  new C {}
}
new B {}

// prints c

vs this:

trait B { self =>
  val v = "b"
  trait C {
    val v = "c"
    println(this.v)  // prints c
    println(self.v)  // prints b
  }
  new C {}
}
new B {}

(Without any further type annotation, you could leave out all of the this in this example.)

OTHER TIPS

The self-type is not shadowing this. It IS the type of this. (Actually the self-type is the intersection: in trait A { self: B => ...} the type of this is A with B.) You can supply a name when giving the self type as a convenience to disambiguate multiple thises, but as you never have more than one this in any of your code snippets, it's irrelevant.

[...] where the self-type's "this" takes precedence over the trait's "this"

You have this same idea several places, that there is more than one this. There isn't. There is only this. You don't get another this until you declare another template.

trait A {
  self1 =>

  trait B {
    self2 =>

    // Here unqualified this refers to B and not A, so "self1" is useful.
    // ...but not necessary, because A.this.xxx does the same thing.
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top