I am creating an Asteroids clone and want to create a move function. I thought I could use pattern matching on data types, but of course the type signature then does not conform to the actual method. I want to use different code if the t parameter in the Moving data type in the move function is of data type Bullet and tried this, but that doesn't work. Any ideas besides making a specialised move function (which might be better here, but I still want to know if there are other ways).
So I have Moving Asteroid and Moving Bullet and want to pattern match on the type of either Asteroid or Bullet (or other types which I didn't post here to give a minimum example)
What the move function should do in one sentence: Use wraparound for moving all types of Moving o except Moving Bullet.
Some contextual code:
data Moving s = Moving {
position :: Position,
velocity :: Velocity,
size :: Float,
specifics :: s
}
data Bullet = Bullet {
damage :: Int
}
| DeadBullet
data Asteroid = Asteroid
| DeadAsteroid
move :: Float -> Moving o -> Moving o
move secs (Moving (x, y) v@(vx, vy) s t@(Bullet _)) = Moving (x', y') v s t
where x' = (x + vx * secs)
y' = (y + vy * secs)
move secs (Moving (x, y) v@(vx, vy) s t) = Moving (x', y') v s t
where x' = (x + vx * secs) `mod'` width
y' = (y + vy * secs) `mod'` height
Error:
src\Controller.hs:100:42: error:
* Couldn't match expected type `o' with actual type `Bullet'
You can't pattern match this way because Moving o is polymorphic. If you had a function that moved only bullets, Moving Bullet would work to pattern match just like this.
There are lots of different ways to get around this.One easy solution, depending on other aspects of your game, would be to bring Bullet and Asteroid into a single Movable data type that you can pattern match on instead:
data Moving = Moving {
position :: Position,
velocity :: Velocity,
size :: Float,
specifics :: Movable
}
data Movable = B Bullet | A Asteroid
data Bullet = Bullet {
damage :: Int
}
| DeadBullet
data Asteroid = Asteroid
| DeadAsteroid
move :: Float -> Moving -> Moving
move secs (Moving (x, y) v@(vx, vy) s t@(B _)) = Moving (x', y') v s t
where x' = (x + vx * secs)
y' = (y + vy * secs)
move secs (Moving (x, y) v@(vx, vy) s t) = Moving (x', y') v s t
where x' = (x + vx * secs) `mod'` width
y' = (y + vy * secs) `mod'` height
I don't disagree with jkeuhlen (or chepner in your first question on this): you may not really need the type distinction at all, can keep it all on the value level.
But you can also do it on the type level, and this does make some sense because moving an object should never change its type. This is now something where you'd use a class. You could just make move a method:
type Time = Float
class ObjSpecific s where
move :: Time -> Moving s -> Moving s
instance ObjSpecific Bullet where
move δt (Moving p v s t) = -- definition without edge-wrapping
instance ObjSpecific Asteroid where
move δt (...) = ... -- definition with edge-wrapping
BTW, I think you should probably do something to get rid of the bullets after they've left the screen... perhaps make it move :: Time -> Moving s -> Maybe (Moving s).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With