Reputation: 1178
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
Upvotes: 6
Views: 1104
Reputation: 3068
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.
Upvotes: 0
Reputation: 26566
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.
Upvotes: 0
Reputation: 61414
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.
Upvotes: 2