Quantcast

Question on case class and copy method

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Question on case class and copy method

linjie nie
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.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Question on case class and copy method

linjie nie
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 -
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.


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Question on case class and copy method

Kevin Wright-4
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 -
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: [hidden email]
wave: [hidden email]
skype: kev.lee.wright
twitter: @thecoda

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Question on case class and copy method

Ken Bloom-2
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/

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Question on case class and copy method

Daniel Sobral
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 -
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.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Question on case class and copy method

Naftoli Gugenheim
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:

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 -
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: [hidden email]
wave: [hidden email]
skype: kev.lee.wright
twitter: @thecoda


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Question on case class and copy method

Josh Suereth
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 ... } ]


2009/12/20 Kevin Wright <[hidden email]>

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 -
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: [hidden email]
wave: [hidden email]
skype: kev.lee.wright
twitter: @thecoda



Loading...