This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Question on case class and copy method

6 replies
linjie nie
Joined: 2009-06-17,
User offline. Last seen 42 years 45 weeks ago.
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.

linjie nie
Joined: 2009-06-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Question on case class and copy method
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 <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
Joined: 2009-06-09,
User offline. Last seen 49 weeks 4 days ago.
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>
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

Ken Bloom
Joined: 2009-09-22,
User offline. Last seen 42 years 45 weeks ago.
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.

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
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:
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.
Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
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>
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


Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 6 days ago.
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:
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



Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland