6

Assume I have a base class

abstract class Base {

  type B<: Base

  def rep:String

  def copy:B
}

class MyBase(override val rep:String) extends Base {
  type B = MyBase

 override def copy = new MyBase(rep)
}

I then try to add another trait as a mixin, for which I want the return type for copy to be the the appropriate type (meaning that calling copy on the mixin returns a mixin type, by setting B to the appropriate type). I haven't been able to get this to compile, or even to understand where the override keyword should go.

Edited: I have refined the example

abstract class Base {


  type B <: Base

  def rep:String

  def copy:B

}

class MyBase(val rep:String) extends Base {

  type B = MyBase

  def copy = new MyBase(rep)
}


trait DecBase extends Base {

  abstract override def rep = "Rep: "+super.rep   
}

My question is, how do I declare an appropriate type B and copy method for DecBase, so that the copy returns a DecBase , and also, why won't this compile?

println(((new MyBase("ofer") with DecBase)).rep)

This is something I would have achieved in Java (with some nastiness, using recursive generic types). I'm sure that it's possible to do something nicer in Scala.

Edit

Using

trait DecBase extends Base {

  override type B = DecBase
  abstract override  val rep= "Dec:"+super.rep
  abstract override def copy = new MyBase(rep) with DecBase
}

I get the following compiler errors

error: overriding type B in class MyBase, which equals com.amadesa.scripts.MyBase;
type B in trait DecBase, which equals com.amadesa.scripts.DecBase has incompatible type
println(((new MyBase("ofer") with DecBase)).rep)

error: overriding type B in class MyBase, which equals com.amadesa.scripts.MyBase;
type B in trait DecBase, which equals com.amadesa.scripts.DecBase has incompatible type
abstract override def copy = new MyBase(rep) with DecBase
user44242
  • 1,168
  • 7
  • 20

3 Answers3

2

I assume your mix in looks something like this

trait MixIn extends Base {
  override B = MixinBase
  override def copy = new MixinBase(rep)
}

I think the override on MyBase is part of the problem. It's unnecessary and confuses the compiler.

If the copy on Base in fact has an implementation, making override necessary, you need to tell the compiler which method to use. If it's not obvious to it, it throws up its hands and raises an error. Try this.

val b = new MyBase(rep) with MixIn {
  override def copy = MixIn.super.copy
}

The MixIn.super.copy is a call to the one you want.

You may want to review this page on Scala Class Linearization to understand what happens when you have competing implementations of a method in a type.

Edit: oh this is a completely different problem. It's the val in case MyBase(val rep:String). You can't override a val with a def because a val is assumed to be immutable. You can override a def or var with a val, but not the other way around. Make it:

trait DecBase extends Base {
  abstract override val rep = "Rep: "+super.rep
}

Please include the compiler error next time. It makes it so much easier to see what the problem is.

sblundy
  • 60,628
  • 22
  • 121
  • 123
  • Edited again, added compiler errors this time. The important thing for me is the correct return type for the copy method. – user44242 Feb 01 '11 at 16:56
  • Now I'm having trouble making it work. To start with, you can't override B to be DecBase because DecBase isn't a subtype of MyBase. But after fixing that, I'm still getting an error. The compiler doesn't seem to like overriding type B = MyBase, but it maybe something else. – sblundy Feb 01 '11 at 17:18
0

I think it's somehow connected to val rep in class MyBase. You should define MyBase abstract, if you are not going to implement def rep.

Here is working example:

abstract class Base {
  type B <: Base
  def rep:String
  def copy: B
}

class MyBase(val repVal: String) extends Base {
  type B = MyBase
  def rep = repVal
  def copy = new MyBase(repVal)
}


trait DecBase extends Base {
  abstract override def rep = "Rep: " + super.rep
}

println(((new MyBase("ofer"))).rep) // prints: ofer
println(((new MyBase("ofer") with DecBase)).rep) // prints: Rep: ofer

Hope this helps.

tenshi
  • 26,268
  • 8
  • 76
  • 90
0

So as I understand it, the goal is to be able to do:

val myBase: MyBase = new MyBase("alone")
val myBaseCopy: MyBase = myBase.copy

val decBase: DecBase = new MyBase("mixed") with DecBase
val decBaseCopy: DecBase = decBase.copy

Lets rewrite your code to use type bounds instead of equality in your type declarations (and also to fix the mismatch between val rep and def rep which is hiding some other compiler errors):

abstract class Base {
  type B <: Base
  def rep: String
  def copy: B
}

class MyBase(_rep: String) extends Base {
  type B <: MyBase
  def rep = _rep
  def copy = new MyBase(rep)
}

trait DecBase extends Base {
  override type B <: DecBase
  override abstract def rep = "Rep: " + super.rep   
}

This now says that MyBase.copy should return a MyBase or a subclass, and DecBase.copy should return a DecBase or a subclass. I believe that's what you want, right? Now the resulting compiler error is clear:

temp.scala:10: error: type mismatch;
 found   : this.MyBase
 required: MyBase.this.B
  def copy = new MyBase(rep)
             ^
one error found

So you're returning a MyBase but you really need to be returning the subclass, e.g. you want to be returning new MyBase(rep) with DecBase instead of new MyBase(rep), but only when your object was declared as new MyBase(...) with DecBase. I don't know any way to do that, though perhaps you might look at adding a trait after an object has been constructed.

Community
  • 1
  • 1
Steve
  • 3,038
  • 2
  • 27
  • 46
  • Does not compile, even if I restrict to Base and MyBase, and forget DecBase. Compiler complains: error: type mismatch; found : com.amadesa.scripts.MyBase required: MyBase.this.B def copy = new MyBase(rep) – user44242 Feb 02 '11 at 11:13
  • 1
    Exactly. You want your `DecBase.copy` to return a `DecBase` but that's not possible because `MyBase` is actually creating the object and `MyBase` knows nothing about `DecBase`. You're expecting `DecBase` to somehow change the line `new MyBase(rep)` to `new MyBase(rep) with DecBase`. – Steve Feb 02 '11 at 12:45