- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Type members vs. types of members
Mon, 2012-02-20, 10:51
If we have
class Aclass B extends A
class X { def foo: A = new A}
The foo declaration says my return type RT is A, if you override me then the new return type must be subtype of A. In other words it says if you override me the new type must be <: A, but if you don't then it is = A. The declaration say something like RT <:= A.
Would it make sense to provide this for type members? The nice thing about it that both type members and types of members (at least "return" types) which both belong to one family (the class) could be made to behave equivalently.
abstract class Y1 { type T <: A}
class Y2 extends Y1 { type T = A}
Y1 doesn't really need to be abstract (since 2.9?) but once T is set in Y2 it can't be overriden anymore. If it were possible to provide the same behavior, I think also the encoding of virtual classes could be simplified:
Example taken from: https://wiki.scala-lang.org/display/SIW/VirtualClassesDesign
/* class A { class C(x: Int) <: { var y = x ; def f(z: Int) = z + 1 } class D(z) extends C(f(z)) { override def f(z:Int) = z + 2 } }*/
abstract class A { type C <: CT
trait CT { self: C => val x: Int; val y = x; def f(z:Int) = z + 1 }
type D <: C with DT
trait DT extends { self: D => def f(z:Int) = z + 2 }
trait preDT extends { self: D => val z: Int; val x = f(z) }
def newC(x: Int): C def newD(x: Int): D }
class Afinal extends A { type C = CT type D = C with DT
class CC(_x:Int) extends { val x = _x } with CT
def newC(x:Int): C = new CC(x)
class DC(_z:Int) extends { val z = _z } with preDT with CT with DT { override def f(z:Int) = super.f(z) }
def newD(z:Int):D = new DC(z) }
would become just:
class A { type C <:= CT
trait CT { self: C => val x: Int; val y = x; def f(z:Int) = z + 1 }
class CC(_x:Int) extends { val x = _x } with CT
def newC(x:Int): C = new CC(x)
type D <:= C with DT
trait DT extends { self: D => def f(z:Int) = z + 2 }
trait preDT extends { self: D => val z: Int; val x = f(z) }
class DC(_z:Int) extends { val z = _z } with preDT with CT with DT { override def f(z:Int) = super.f(z) }
def newD(z:Int):D = new DC(z) }
With regards,Jan
class Aclass B extends A
class X { def foo: A = new A}
The foo declaration says my return type RT is A, if you override me then the new return type must be subtype of A. In other words it says if you override me the new type must be <: A, but if you don't then it is = A. The declaration say something like RT <:= A.
Would it make sense to provide this for type members? The nice thing about it that both type members and types of members (at least "return" types) which both belong to one family (the class) could be made to behave equivalently.
abstract class Y1 { type T <: A}
class Y2 extends Y1 { type T = A}
Y1 doesn't really need to be abstract (since 2.9?) but once T is set in Y2 it can't be overriden anymore. If it were possible to provide the same behavior, I think also the encoding of virtual classes could be simplified:
Example taken from: https://wiki.scala-lang.org/display/SIW/VirtualClassesDesign
/* class A { class C(x: Int) <: { var y = x ; def f(z: Int) = z + 1 } class D(z) extends C(f(z)) { override def f(z:Int) = z + 2 } }*/
abstract class A { type C <: CT
trait CT { self: C => val x: Int; val y = x; def f(z:Int) = z + 1 }
type D <: C with DT
trait DT extends { self: D => def f(z:Int) = z + 2 }
trait preDT extends { self: D => val z: Int; val x = f(z) }
def newC(x: Int): C def newD(x: Int): D }
class Afinal extends A { type C = CT type D = C with DT
class CC(_x:Int) extends { val x = _x } with CT
def newC(x:Int): C = new CC(x)
class DC(_z:Int) extends { val z = _z } with preDT with CT with DT { override def f(z:Int) = super.f(z) }
def newD(z:Int):D = new DC(z) }
would become just:
class A { type C <:= CT
trait CT { self: C => val x: Int; val y = x; def f(z:Int) = z + 1 }
class CC(_x:Int) extends { val x = _x } with CT
def newC(x:Int): C = new CC(x)
type D <:= C with DT
trait DT extends { self: D => def f(z:Int) = z + 2 }
trait preDT extends { self: D => val z: Int; val x = f(z) }
class DC(_z:Int) extends { val z = _z } with preDT with CT with DT { override def f(z:Int) = super.f(z) }
def newD(z:Int):D = new DC(z) }
With regards,Jan
Mon, 2012-02-20, 16:51
#2
Re: Type members vs. types of members
On Mon, Feb 20, 2012 at 4:20 PM, Paul Phillips <paulp@improving.org> wrote:
On Mon, Feb 20, 2012 at 1:51 AM, Jan Vanek <j3vanek@googlemail.com> wrote:abstract class Y1 { type T <: A}
class Y2 extends Y1 { type T = A}
Y1 doesn't really need to be abstract (since 2.9?) but once T is set in Y2 it can't be overriden anymore.
Not since 2.9, it has always been like that.
You couldn't do this without attaching the same variance restrictions which apply to type parameters. Because if Y1 looks like this
abstract class Y1 { type T <: A ; def f(x: T) = x }
Then T, once fixed, must stay fixed.
abstract class Y1 { type T <:= A ; def f(x: T) = x }
Using such T in invariant or contra-variant position would effectively fix T to A. A warning should be issued.
Regards, Jan
Mon, 2012-02-20, 18:21
#3
Re: Type members vs. types of members
On 20.02.2012 16:47, Jan Vanek wrote:
This was a bit too rash... Thought about it on the way home, and need some more, thanks.
KdAONFkSer1bC6vAJ1uCPU4yWx4NhHZLWvP61Q [at] mail [dot] gmail [dot] com" type="cite">
On Mon, Feb 20, 2012 at 4:20 PM, Paul Phillips <paulp [at] improving [dot] org" rel="nofollow">paulp@improving.org> wrote:
On Mon, Feb 20, 2012 at 1:51 AM, Jan Vanek <j3vanek [at] googlemail [dot] com" target="_blank" rel="nofollow">j3vanek@googlemail.com> wrote:
abstract class Y1 { type T <: A }
class Y2 extends Y1 { type T = A }
Y1 doesn't really need to be abstract (since 2.9?) but once T is set in Y2 it can't be overriden anymore.
Not since 2.9, it has always been like that.
You couldn't do this without attaching the same variance restrictions which apply to type parameters. Because if Y1 looks like this
abstract class Y1 { type T <: A ; def f(x: T) = x }
Then T, once fixed, must stay fixed.
abstract class Y1 { type T <:= A ; def f(x: T) = x }
Using such T in invariant or contra-variant position would effectively fix T to A. A warning should be issued.
This was a bit too rash... Thought about it on the way home, and need some more, thanks.
On Mon, Feb 20, 2012 at 1:51 AM, Jan Vanek <j3vanek@googlemail.com> wrote:
Not since 2.9, it has always been like that.
You couldn't do this without attaching the same variance restrictions which apply to type parameters. Because if Y1 looks like this
abstract class Y1 { type T <: A ; def f(x: T) = x }
Then T, once fixed, must stay fixed.