Reputation: 87
I'm doing the following function to return a new figure from another, but Scala is inferring the result as Figure and I want it to be the figure in particular, as a circle, etc. How could I do to infer the particular figure? I have been told to use generics to solve it, how would this be?
trait Figure {
def x:Int
def y:Int
}
case class Circle(x:Int, y: Int, radio: Double)
extends Figure
case class Rectangle(x:Int, y: Int, width: Int, high: Int)
extends Figure
object Motor {
def move[T](x: Int, y: Int, figure: T) :Figure = figure match {
case Circle(xPos, yPos, radio) => Circle(xPos+x, yPos+y, radio)
case Rectangle(xPos, yPos, width, high) => Rectangle(xPos+x, yPos+y, width, high)
}
}
Upvotes: 3
Views: 102
Reputation: 14083
Here's is a more concise, perhaps a bit less intimidating version of Sarvesh Kumar Singh's suggestion to use a typeclass. I think that is the best approach all around. It gives you typesafe functionality while letting you keep your basic types very simple.
trait Figure {
def x:Int
def y:Int
}
case class Circle(x:Int, y: Int, radius: Double) extends Figure
case class Rectangle(x:Int, y: Int, width: Int, height: Int) extends Figure
trait Movable[T] {
def move( x: Int, y: Int, movable: T ) : T
}
implicit final object CircleIsMovable extends Movable[Circle] {
def move( x: Int, y: Int, c: Circle ) = Circle( c.x + x, c.y + y, c.radius )
}
implicit final object RectangleIsMovable extends Movable[Rectangle] {
def move( x: Int, y: Int, r: Rectangle ) = Rectangle( r.x + x, r.y + y, r.width, r.height )
}
object Motor {
def move[T : Movable](x: Int, y: Int, movable: T) : T = implicitly[Movable[T]].move( x, y, movable )
}
Then...
scala> Motor.move(10,10,Circle(0,0,1))
res1: Circle = Circle(10,10,1.0)
scala> Motor.move(10,10,Rectangle(0,0,1,1))
res2: Rectangle = Rectangle(10,10,1,1)
Upvotes: 2
Reputation: 42474
You may want to consider moving the implementation of move
to the various classes. Here is an example that uses abstract types to enable the method to return the type of the object:
trait Figure {
def x: Int
def y: Int
type Self <: Figure
def move(dx: Int, dy: Int): Self
}
case class Circle(x: Int, y: Int, radius: Double) extends Figure {
type Self = Circle
def move(dx: Int, dy: Int): Circle = copy(x = x + dx, y = y + dy)
}
case class Rectangle(x: Int, y: Int, widht: Int, height: Int) extends Figure {
type Self = Rectangle
def move(dx: Int, dy: Int): Rectangle = copy(x = x + dx, y = y + dy)
}
Upvotes: 0
Reputation: 13985
You should make it so that the "move"
happens on the type
T
itself and return type
T
. But then the compiler will complain about not being sure that you are returning a T
because the actual type of T
will be determined for the use of move
and compiler has no evidence to determine that it was a Circle
as match-case
is a runtime thing.
Which means you need to provide evidence which can be used at compile-time to move
any instance of type
T
.
import scala.language.implicitConversions
trait Figure {
def x:Int
def y:Int
}
case class Circle(x:Int, y: Int, radio: Double)
extends Figure
case class Rectangle(x:Int, y: Int, width: Int, high: Int)
extends Figure
Now, let us build the required evidence which will be used to "enrich" our Figure
instances
trait MoveSupport[F <: Figure] {
val f: F
def move(x: Int, y: Int): F
}
object MoveSupport {
class CircleMoveSupport(val f: Circle) extends MoveSupport[Circle] {
override def move(x: Int, y: Int): Circle = f.copy(x = f.x + x, y = f.y + y)
}
class RectangleMoveSupport(val f: Rectangle) extends MoveSupport[Rectangle] {
override def move(x: Int, y: Int): Rectangle = f.copy(x = f.x + x, y = f.y + y)
}
implicit def toCircleMoveSupport(circle: Circle) = new CircleMoveSupport(circle)
implicit def toRectangleMoveSupport(rectangle: Rectangle) = new RectangleMoveSupport(rectangle)
}
Now, we can use these evidence to "enrich" our Figure
types to have move
support.
import MoveSupport._
val circle = Circle(1, 1, 1)
// circle: Circle = Circle(1,1,1.0)
val circle2 = circle.move(1, 1)
// circle2: Circle = Circle(2,2,1.0)
Or, you can build your Motor
using these evidence.
object Motor {
import MoveSupport._
def move[T <: Figure](x: Int, y: Int, figure: T)(implicit ev: T => MoveSupport[T]): T = figure.move(x, y)
}
val c = Circle(1, 1, 1)
// circle: Circle = Circle(1,1,1.0)
val c1 = Motor.move(1, 1, c)
// circle1: Circle = Circle(2,2,1.0)
Upvotes: 1
Reputation: 14083
Maybe what you are after is something like
object Motor {
def move[T <: Figure](x: Int, y: Int, figure: T): T = {
val moved = figure match {
case Circle(xPos, yPos, radio) => Circle(xPos+x, yPos+y, radio)
case Rectangle(xPos, yPos, width, high) => Rectangle(xPos+x, yPos+y, width, high)
}
moved.asInstanceOf[T]
}
}
Upvotes: 0