Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Pattern matching on different data types

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'
like image 298
The Coding Wombat Avatar asked May 30 '26 03:05

The Coding Wombat


2 Answers

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
like image 75
jkeuhlen Avatar answered Jun 01 '26 15:06

jkeuhlen


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

like image 45
leftaroundabout Avatar answered Jun 01 '26 15:06

leftaroundabout



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!