- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Question on case class and copy method
Sun, 2009-12-20, 16:25
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 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.
Sun, 2009-12-20, 16:57
#2
Re: Question on case class and copy method
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.8Make 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 <nielinjie [at] gmail [dot] com>
--
Kevin Wright
mail/google talk: kev [dot] lee [dot] wright [at] googlemail [dot] com
wave: kev [dot] lee [dot] wright [at] googlewave [dot] com
skype: kev.lee.wright
twitter: @thecoda
1. case classes shouldn't subclass other case classes. This can cause lots of problems and is formally deprecated in 2.8Make 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 <nielinjie [at] gmail [dot] com>
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.
--
Kevin Wright
mail/google talk: kev [dot] lee [dot] wright [at] googlemail [dot] com
wave: kev [dot] lee [dot] wright [at] googlewave [dot] com
skype: kev.lee.wright
twitter: @thecoda
Sun, 2009-12-20, 20:17
#3
Re: Question on case class and copy method
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.
Sun, 2009-12-20, 22:07
#4
Re: Question on case class and copy method
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 <nielinjie [at] gmail [dot] com> wrote:
--
Daniel C. Sobral
I travel to the future all the time.
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 <nielinjie [at] gmail [dot] com> 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.
--
Daniel C. Sobral
I travel to the future all the time.
Tue, 2009-12-22, 05:27
#5
Re: Question on case class and copy method
I think you can write [S <: Shape{ def copy ... } ]
2009/12/20 Kevin Wright <kev [dot] lee [dot] wright [at] googlemail [dot] com>
2009/12/20 Kevin Wright <kev [dot] lee [dot] wright [at] googlemail [dot] com>
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 <nielinjie [at] gmail [dot] com>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.
--
Kevin Wright
mail/google talk: kev [dot] lee [dot] wright [at] googlemail [dot] com
wave: kev [dot] lee [dot] wright [at] googlewave [dot] com
skype: kev.lee.wright
twitter: @thecoda
Tue, 2009-12-22, 06:07
#6
Re: Question on case class and copy method
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 <naftoligug [at] gmail [dot] com> wrote:
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 <naftoligug [at] gmail [dot] com> wrote:
I think you can write [S <: Shape{ def copy ... } ]
2009/12/20 Kevin Wright <kev [dot] lee [dot] wright [at] googlemail [dot] com>
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 <nielinjie [at] gmail [dot] com>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.
--
Kevin Wright
mail/google talk: kev [dot] lee [dot] wright [at] googlemail [dot] com
wave: kev [dot] lee [dot] wright [at] googlewave [dot] com
skype: kev.lee.wright
twitter: @thecoda
2009/12/20 linjie nie <nielinjie [at] gmail [dot] com>