Quantcast

Newbie question: Limiting where traits can be mixed in?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Newbie question: Limiting where traits can be mixed in?

Jonathan Merritt-2
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.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Newbie question: Limiting where traits can be mixed in?

Eric Willigers
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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Newbie question: Limiting where traits can be mixed in?

Jorge Ortiz
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.
>
>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Newbie question: Limiting where traits can be mixed in?

James Iry-2
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

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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Newbie question: Limiting where traits can be mixed in?

Alex Boisvert-3
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

 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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Newbie question: Limiting where traits can be mixed in?

Jorge Ortiz
> // This doesn't work
> class A
> trait B extends A

Huh? This works for me...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Newbie question: Limiting where traits can be mixed in?

Alex Boisvert-3
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

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Newbie question: Limiting where traits can be mixed in?

Jorge Ortiz
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
>
>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Newbie question: Limiting where traits can be mixed in?

Aaron Harnly-2-2
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




Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Re: Newbie question: Limiting where traits can be mixed in?

Alex Boisvert-3
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:
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





Loading...