3

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) } } 
4
  • So... what is the issue with this code ? Other than typos (are these typos ?) in Circulo and Rectangulo Commented Dec 11, 2017 at 6:19
  • I am assuming that you are talking about the result of move being a Figure and not a concrete class like Circle or Rectangle. To get to the underlying concrete class you can use pattern matching as you have done in the method above. Commented Dec 11, 2017 at 6:33
  • @SarveshKumarSingh the function move returns Figure but I want to return the figure in particular as circle, rectangle, etc. Commented Dec 11, 2017 at 6:38
  • @ThePretendProgrammer That's exactly what I want, but the result of the function is Figure and I want it to be the specific figure, now I'm solving it by casting in the test but I want the function to do it alone. Commented Dec 11, 2017 at 6:43

4 Answers 4

2

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) 
Sign up to request clarification or add additional context in comments.

2 Comments

This looks good, I'd just move the typeclass instances into either the Movable or the shape subtype companion objects.
Yes. Ordinarily I'd put the implicit instances in the Movable companion object. But I thought that'd look more intimidating to people who might not have encountered this style before.
1

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) 

Comments

0

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] } } 

4 Comments

It looks good. why [T <: Figure? thanks, tomorrow I will try it.
That just restricts the type of T to Figure or some subtype of Figure. (The typeclass-ish solution suggested by Sarvesh Kumar Singh below is better -- it is much more typesafe. In your approach and my modification of it, someone could call move on a new subtype of Figure that Motor.move(...) does not support, leading to runtime errors where we would prefer compile time errors. Sarvesh's solution looks a bit more intimidating than it needs to be, it can be implemented a bit more concisely.)
moved.asInstanceOf[T] all bets are off with that. I would definitely stray away from that.
@YuvalItzchakov I entirely agree. I was just trying to make minimal corrections to get the approach used by the original poster to "work", but it is not the best approach. Sarvesh Kumar Singh's suggestion is much better, and now I've also posted a similar, a bit more concise (and so hopefully less intimidating) approach.
0

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) } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.