Reputation: 3130
Say I want to define a ball's characteristics via traits:
trait Size {
val size: String
}
trait Big extends Size {
val size = "big"
}
trait Small extends Size {
val size = "small"
}
trait Bouncy {
def bounce: Unit = println("boing boing boing")
}
class Ball
val bigBouncyBall = new Ball with Big with Bouncy
So far, so good. But how can I mutate my ball's size while preserving its bounciness?
def shrink(ball: Ball) =
ball with Small // invalid syntax. Does not compile
def shrink(ball: Ball) =
ball.asInstanceOf[Ball with Small] // Nope. Ball no longer bounces!
In other words, can I override a specific trait while preserving others?
Upvotes: 1
Views: 230
Reputation: 40508
You are mixing types of objects with properties. Traits are intended to represent the former while instance members are for the latter. Can you "mutate" a pear into an apple? That doesn't make sense, right?
Something like this:
case class Ball(isBig: Boolean, isBouncy: Boolean) {
def shrink = copy(isBig = false)
}
Seems like a more sensible implementation in your case. Note: this is still not mutating the instance per se (it is generally not a good idea in scala, and should be avoided), but rather returns a different instance with the modified property. You could make it mutate in place instead, but, like I said, it's not a good idea, so, I won't go there ...
Now, which features are attributes of a type, and which are properties of an instance is not really set in stone, that depends on the needs of the application that will use your data model.
For example, if the app deals with different shapes, but only cares about the number of vertices, it might just have
class Shape(val vertices: Int)
val circle = new Shape(0)
val rect = new Shape(4)
etc., but if it gets more involved with the properties of specific shapes, it might need more specialized traits like Polygon
or Regular
, and classes like
trait Regular { def size: Float }
case class Circle(val size: Float) extends Shape(0) with Regular
case class Square(val size: Float) extends Shape(4) with Regular with Polygon
This comes with "pros" and "cons". On one hand, you can now create robust specialized functions, that only deal with particular kinds of shapes:
def perimeter(p: Polygon with Regular) = p.size * p.vertices
def area(p: Polygon with Regular) = p.vertices * Math.pow(p.size, 2)/ (4 * Math.tan(Math.PI/p.vertices))
But can no longer "mutate" a circle into a square - it simply makes no sense.
Upvotes: 2