Suppose I have an object which has a point denoting it's location and shape.
type Point = (Double, Double)
data Object =
Object { location :: Point
, shape :: Shape }
where a shape might be like
data Shape
= Circle { radius :: Double }
| Rectangle { height :: Double, width :: Double }
Suppose I have a function which can draw objects.
draw :: [Object] -> IO ()
So far here are no problems.
Now, suppose I want objects to move. So objects should have a velocity.
type Velocity = (Double, Double)
data Object =
Object { location :: (Double, Double)
, shape :: Shape
, velocity :: Velocity }
type Time = Double
move :: Time -> Object -> Object
moveAll :: Time -> [Object] -> [Object]
The problem is that I need a velocity field to move objects, but I don't need it to draw them.
I think that allowing draw
function to know about object's velocity breaks
abstraction. Am I right?
How to combine previous object definition with extra velocity field in order to move objects?
- To use tuples like
(Velocity, Object)
? - To wrap both into new data type? If so, then how to call that data type?
Any suggestions are appreciated.
2 Answers 2
You could declare a typeclass for objects that know their location and shape, and create an instance for Object
. Then your draw
would accept any instance of that typeclass, but wouldn't be able to find out about any other fields in Object
.
Besides using tuples or wrapping Object
in a new datatype, another option is to parameterize Object
with a type variable:
data Object v =
Object { location :: (Double, Double)
, shape :: Shape
, velocity :: v } deriving (Functor)
When passing an object to the renderer, you could "mute" the velocity by storing a ()
, using void myObject
or perhaps using a polymorphic update like myObject { velocity = () }
.
-
2If the renderer has type
[Object v] -> IO ()
, then you know that it cannot use the fact that whenever you call it you actually pass a[Object Velocity]
, and there is no need for void.Gurkenglas– Gurkenglas2016年07月11日 16:45:19 +00:00Commented Jul 11, 2016 at 16:45
MovableObject
that containsObject
plus its velocity. Most of the time it's better to have a dedicated, named data type rather than using tuples. Whether or not making velocity available todraw
depends on what exactly you want to draw. For example if you decide to draw arrows representing velocity, it would be appropriate.lens
provides tools to interact with deeply nested data structures.