Hi Everyone,
I have what I hope is a simple newbie question regarding self typing of traits: Is self-typing of traits the "correct" way to limit the classes into which a trait can be mixed (and as as a side-effect, to give the trait an explicit type which arises from its mixin)? For example, consider the following: class ParentClass { def x = 42 } def f(k: ParentClass) { println(k.x) } trait ConstrainedTrait { self: ParentClass => def y = x def doSomething { f(this) } # f(this) should only work if this.isInstanceOf[ParentClass] } val m = new ParentClass with ConstrainedTrait println(m.y) m.doSomething In this example, ConstrainedTrait appears only to be able to be mixed- in to ParentClass and its subclasses. However, I'm not sure if this is the Right Way (TM). My usage scenario is that I have a trait that I want to mix in to some subclasses of scala.swing.Component. I started out by explicitly re- defining as abstract the parts of the components that I needed, but realized that there are then limitations if the trait itself is to be regarded as a scala.swing.Component type. Then I stumbled across self- typing. It seems to do the trick, but I'm not sure if I'm using it as intended in the case of traits. I have a copy of the Programming in Scala book (Odersky, Spook and Venners), but it doesn't seem to cover this topic of limiting how mixin of traits can occur, or of allowing a trait to "shadow" the type of its mixin class. Thanks, -- Jonathan Merritt PhD, Research Fellow, Department of Mechanical Engineering, The University of Melbourne. |
Jonathan Merritt wrote:
> Is self-typing of traits the "correct" way to limit the classes into > which a trait can be mixed (and as as a side-effect, to give the trait > an explicit type which arises from its mixin)? > trait ConstrainedTrait { > self: ParentClass => > def doSomething { f(this) } # f(this) should only work if > this.isInstanceOf[ParentClass] > In this example, ConstrainedTrait appears only to be able to be mixed-in > to ParentClass and its subclasses. However, I'm not sure if this is the > Right Way (TM). G'day Jonathan, Yes, this would be correct. You had the option of writing f(self) instead of f(this) as "self ... =>" declared "self" as an alias for "this". Alternately, the alias was redundant so with recent versions of Scala you can write "this: ParentClass =>" - the Right Way when no alias is needed. Eric Willigers Wollongong, NSW |
In reply to this post by Jonathan Merritt-2
You can also have
trait ConstrainedTrait extends scala.swing.Component The differences between subclassing and self-typing are subtle. Both will enforce that all instances of ConstrainedTrait are also instances of Component. If you subclass, then the compiler will recognize that every instance of type ConstrainedTrait is also an instance of type Component. If you self-type, then the compiler will only recognize this fact within the body of ConstrainedTrait. There are other differences, but I'm not sure that I'm aware of all of them. Anyone else care to weigh in on when each approach is more appropriate? (You might also want to read the paper "Scalable Component Abstractions" by Odersky and Zenger.) --j On Tue, Jul 29, 2008 at 6:49 PM, Jonathan Merritt <[hidden email]> wrote: > Hi Everyone, > > I have what I hope is a simple newbie question regarding self typing of > traits: > Is self-typing of traits the "correct" way to limit the classes into which a > trait can be mixed (and as as a side-effect, to give the trait an explicit > type which arises from its mixin)? > > For example, consider the following: > > class ParentClass { > def x = 42 > } > > def f(k: ParentClass) { println(k.x) } > > trait ConstrainedTrait { > self: ParentClass => > def y = x > def doSomething { f(this) } # f(this) should only work if > this.isInstanceOf[ParentClass] > } > > val m = new ParentClass with ConstrainedTrait > println(m.y) > m.doSomething > > In this example, ConstrainedTrait appears only to be able to be mixed-in to > ParentClass and its subclasses. However, I'm not sure if this is the Right > Way (TM). > > My usage scenario is that I have a trait that I want to mix in to some > subclasses of scala.swing.Component. I started out by explicitly > re-defining as abstract the parts of the components that I needed, but > realized that there are then limitations if the trait itself is to be > regarded as a scala.swing.Component type. Then I stumbled across > self-typing. It seems to do the trick, but I'm not sure if I'm using it as > intended in the case of traits. I have a copy of the Programming in Scala > book (Odersky, Spook and Venners), but it doesn't seem to cover this topic > of limiting how mixin of traits can occur, or of allowing a trait to > "shadow" the type of its mixin class. > > Thanks, > > -- > Jonathan Merritt PhD, Research Fellow, > Department of Mechanical Engineering, > The University of Melbourne. > > |
The important difference is that self types allow you to mix from any point in a hierarchy rather than inherit from a single class
trait ConstraintTrait { self: scala.swing.Component => ... class MyComponent extends scala.swing.Component class MyConstrainedComponent extends MyComponent with ConstrainedTrait {... On Tue, Jul 29, 2008 at 8:09 PM, Jorge Ortiz <[hidden email]> wrote: You can also have |
In reply to this post by Jorge Ortiz
Hi Jorge,
Two important aspects of self-types worth mentioning, 1) They can break cyclic dependencies between traits // This works trait A { this: B => } trait B { this: A => } // This doesn't work trait A extends B trait B extends A 2) They can require classes to be plugged-in, effectively restricting where in the inheritance hierarchy a trait can be attached to. // This works class A trait B { this: A => } // This doesn't work class A trait B extends A alex On Tue, Jul 29, 2008 at 8:09 PM, Jorge Ortiz <[hidden email]> wrote: You can also have |
> // This doesn't work
> class A > trait B extends A Huh? This works for me... |
On Wed, Jul 30, 2008 at 12:44 PM, Jorge Ortiz <[hidden email]> wrote:
Interesting, so it's possible with classes with no arguments (which are technically traits...). BTW, this also "works", class A(i: Int) trait B extends A but then, new B {} // error: wrong number of arguments for constructor A: (Int)A trying with an argument give the correct message, trait B extends A(1) // error: parents of traits may not have parameters alex |
Right, but B can be mixed in:
new A(1) with B --j On Wed, Jul 30, 2008 at 2:48 PM, Alex Boisvert <[hidden email]> wrote: > On Wed, Jul 30, 2008 at 12:44 PM, Jorge Ortiz <[hidden email]> wrote: >> >> > // This doesn't work >> > class A >> > trait B extends A >> >> Huh? This works for me... > > Interesting, so it's possible with classes with no arguments (which are > technically traits...). > > BTW, this also "works", > > class A(i: Int) > trait B extends A > > but then, > > new B {} // error: wrong number of arguments for constructor A: (Int)A > > trying with an argument give the correct message, > > trait B extends A(1) // error: parents of traits may not have parameters > > alex > > |
Jorge Ortiz <jorge.ortiz@...> writes:
> Right, but B can be mixed in: > new A(1) with B To complete the thought, this is the same restriction (though with different errors on nonconformance) that selftyping will impose: -- selftyping -- class A(x: Int) trait B { this: A => } scala> new B { } <console>:7: error: illegal inheritance; self-type java.lang.Object with B does not conform to B's selftype B with A new B { } ^ scala> new A(1) with B res1: A with B = $anon$1@c34565 -- inheriting -- scala> trait B2 extends A defined trait B2 scala> new B2 { } <console>:7: error: wrong number of arguments for constructor A: (Int)A new B2 { } ^ scala> new A(1) with B2 res3: A with B2 = $anon$1@3b56ce ~aaron |
I see, thanks for the clarifications guys. Somehow I thought classes were more special :)
On Wed, Jul 30, 2008 at 2:56 PM, Aaron Harnly <[hidden email]> wrote:
|
Free forum by Nabble | Edit this page |