|
Hi list -
I have some code like this - case class Shape(val color:String) case class Circle(val radius:Int,override val color:String) extends Shape(color) case class Square(val size:Int,override val color:String) extends Shape(color) class PaintingWorkshop[S<:Shape]{ val color="red" var shapes=new scala.collection.mutable.ListBuffer[S]() def put(shape:S){ shapes+=shape.copy(color="red") //error } } Compiler dose not like it, saying found type Shape but required S, at the line i marked. |
|
I think the problem is S<:Shape, it should be something like "S<:Shape and have copy method", but I dont know how to define it.
2009/12/20 linjie nie <[hidden email]> Hi list - |
|
In reply to this post by linjie nie
A couple of problems here:
1. case classes shouldn't subclass other case classes. This can cause lots of problems and is formally deprecated in 2.8 Make Shape into an abstract class sealed abstract class Shape { def color:String } 2. why is the shapes collection both a var and a mutable collection? This mixes of idioms, choose one or the other To answer your original question, to copy the object you can pattern match on it: def put(shape:S) : Unit = shapes += shape match { case s:Circle => s.copy(color="red")
case s:Square => s.copy(color="red") } There's no need to match the default case as Shape is sealed and you want the compiler to help you out here and warn of shapes you've missed
2009/12/20 linjie nie <[hidden email]> Hi list - -- Kevin Wright mail/google talk: [hidden email] wave: [hidden email] skype: kev.lee.wright twitter: @thecoda |
|
In reply to this post by linjie nie
On Sun, 20 Dec 2009 23:25:05 +0800, linjie nie wrote:
> Hi list - > I have some code like this - > case class Shape(val color:String) > case class Circle(val radius:Int,override val color:String) extends > Shape(color) > case class Square(val size:Int,override val color:String) extends > Shape(color) > class PaintingWorkshop[S<:Shape]{ > val color="red" > var shapes=new scala.collection.mutable.ListBuffer[S]() def > put(shape:S){ > shapes+=shape.copy(color="red") //error > } > } > > Compiler dose not like it, saying found type Shape but required S, at > the line i marked. Though the return type of copy() is covariant, so that Square#copy returns a Square and Circle#copy returns a Circle, it's legal to write the following class that doesn't take advantage of the covariance: class Triangle(val size:Int,color:String) extends Shape(color){ def copy:Shape = ... } Thus, when you have a type upper bound [here, S<:Shape], the compiler can only verify what it knows about the Shape class (which is that Shape#copy returns Shape). You can't put the Shape into a collection of S, because S can be constrained to be a particular subtype of Shape, but the return value has no such guarantee. I don't know any solution to this problem. -- Chanoch (Ken) Bloom. PhD candidate. Linguistic Cognition Laboratory. Department of Computer Science. Illinois Institute of Technology. http://www.iit.edu/~kbloom1/ |
|
In reply to this post by linjie nie
You said S is a subclass of Shape, but then tried to insert a Shape (which is the result of the "copy" method on Shape) on it.
That's the same of declaring a variable to be String, a subclass of AnyRef, and then trying to put an AnyRef on it.
Instead of declaring ListBuffer as type S, you should have declared it as type Shape. Of course, the copy method called will be Shape's, not Circle's or Square's, both of which have a different type signature anywya, so you won't get a Circle or Square properly copied. That's not really related to declaring ListBuffer as Shape, however.
Instead of relying on the case class copy method named and default parameters, which _cannot_ help you here, as long as you treat the object as a "shape", you should provide your own copy method, receiving a Map[String,String], returning a Shape, and overridden in Circle and Square.
That would really be best, because case classes don't work well in an hierarchy. The problem with your copy method is, in fact, an example of where case classes break down. On Sun, Dec 20, 2009 at 1:25 PM, linjie nie <[hidden email]> wrote: Hi list - -- Daniel C. Sobral I travel to the future all the time. |
|
In reply to this post by Kevin Wright-4
I think you can write [S <: Shape{ def copy ... } ]
2009/12/20 Kevin Wright <[hidden email]> A couple of problems here: |
|
The biggest issue is that the code you *want* is not really how scala generic work, more like C++ templates. In C++, templates are evaluated at call sites, and needed code is generated for you. In scala, the code is compiled and then call sites make use of this generic form (i.e. there is erasure).
So.... I don't think you can define a type that will use the specific copy method of the class (i.e. with all the appropriate defaults), without going through some extra hoops. You have to ensure that you're using normal runtime-polymorphism. sealed trait Shape[T <: Shape] { def color : String def copy(color : String) : T } case class Circle(radius : Int, color : String) extends Shape { override def copy(color : String) = Circle(radius,color) } class PaintingWorkshop[S<:Shape]{ val color="red" var shapes=new scala.collection.mutable.ListBuffer[S]() def put(shape:S){ shapes+=shape.copy(color="red") } } scala> val pw = new PaintingWorkshop[Circle] pw: PaintingWorkshop[Circle] = PaintingWorkshop@1e88c7f scala> val x = Circle(5,"red") x: Circle = Circle(5,red) scala> pw.put(x) scala> pw.shapes res2: scala.collection.mutable.ListBuffer[Circle] = ListBuffer(Circle(5,red)) ================================================ An alternative to this would be start using some implicit magic. //This trait exists solely for the Painting Workshop trait ShapeCopier[T] { def copy(shape : T)(color : String) :T } class PaintingWorkshop { val color="red" var shapes=new scala.collection.mutable.ListBuffer[Shape]() //An implicit copier trait needs to be available with information on how to copy a shape correctly }def put[S <: Shape](shape:S)(implicit copier : ShapeCopier[S]){ shapes+=copier.copy(shape)(color="red") } case class Circle(radius : Int, color : String) case class Square(length : Int, width : Int, color : String) //Ideally we'd place implicits in the companion objects, but for now we'll put it in a package object package object painting { implicit val circleCopier = new ShapeCopier[Circle] { def copy(shape : Circle)(color : String) = shape.copy(color = color) } implicit val squareCopier = new SquareCopier[Square] { def copy(shape : Square)(color : String) = shape.copy(color = color) } } Either way, it's not quite the same as in C++ where the compiler will write the code for you. Anyway, as an aside, the PaintingWorkshop (as defined) is not very useful. If it's designed to work against shapes, the Type constraints on it limit it to only working with a particular shape subclass. Perhaps describing the goal you're trying to achieve would be more useful? On Mon, Dec 21, 2009 at 11:20 PM, Naftoli Gugenheim <[hidden email]> wrote: I think you can write [S <: Shape{ def copy ... } ] |
| Powered by Nabble | Edit this page |
