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?
Your problem is not the name of the self type (in all your examples both
thisand the alternate self-type name refer to the very same thing and have the same type, namely ‘the greatest lower bound ofBandA’ [§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:
So, even though, you don’t get an error in defining
B, you’re simply not able to use it anyway.This would work
Here, you are explicitly overriding
A’svinB. (Note thatnew B with A {}would fail becauseBneeds to come last.) Also, it has to be avalbecause in most cases you cannot really overridevars and you cannot override something else using avar.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, boththisand 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 insideB, and you needed to refer to the instance ofBinside that trait, you’d need another name for your self-type.Consider this
vs this:
(Without any further type annotation, you could leave out all of the
thisin this example.)