Сопоставление с шаблоном Haskell для разных типов данных - PullRequest
0 голосов
/ 23 октября 2018

Я создаю клон Астероидов и хочу создать функцию перемещения.Я думал, что мог бы использовать сопоставление с образцом для типов данных, но, конечно, сигнатура типа тогда не соответствует фактическому методу. Я хочу использовать другой код, если параметр t в типе данных Moving в функции move имеет тип данных Bullet и пробовал это, но это не работает. Любойидеи, помимо создания специализированной функции перемещения (что может быть лучше, но я все еще хочу знать, есть ли другие способы).

Итак, у меня есть Moving Asteroid и Moving Bullet и я хочу сопоставить шаблон натип Asteroid или Bullet (или другие типы, которые я не разместил здесь, чтобы привести минимальный пример)

Что должна делать функция move в одном предложении: Использовать переносдля перемещения всех типов Moving o, кроме Moving Bullet.

Некоторый контекстный код:

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

Ошибка:

src\Controller.hs:100:42: error:
    * Couldn't match expected type `o' with actual type `Bullet'

Ответы [ 3 ]

0 голосов
/ 23 октября 2018

Я не согласен с jkeuhlen (или chepner в вашем первом вопросе по этому ): вам может вообще не понадобиться различие типов, вы можете оставить все это на уровне значения.

Но вы можете также делать это на уровне типов, и это имеет некоторый смысл, потому что перемещение объекта никогда не должно менять его тип.Теперь это то, где вы будете использовать класс.Вы могли бы просто сделать move метод:

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

Кстати, я думаю, вам, вероятно, следует что-то сделать, чтобы избавиться от пуль после того, как они покинули экран ... возможно, сделайте это move :: Time -> Moving s -> Maybe (Moving s).

0 голосов
/ 23 октября 2018

В дополнение к ответу jkeuhlen вы также можете использовать класс типов:

class Moveable a where
    move :: Float -> a -> a
    position :: a -> Position
    velocity :: a -> Velocity

data Asteroid = Asteroid {
      asteroidP :: Position,
      asteroidV :: Velocity
   }

instance Moveable Asteroid where
    move secs (Asteroid (x, y) v@(vx, vy)) = 
       Asteroid ((x + secs*vx) `mod'` width, (y + secs*vy) `mod'` height) v
    position = asteroidP
    velocity = asteroidV

И аналогично для Bullet.

Это выглядит аналогичноОО-подход наследования, с которым вы можете быть знакомы.Однако имейте в виду, что Moveable в этом коде является набором типов, но не является самим типом.Вы не можете создать список Подвижных вещей и поместить в него как астероиды, так и пули.Пуля и Астероид остаются разными типами.Если вы хотите поместить их обоих в список, то вы должны использовать подход jkeulen (конечно, нет ничего плохого в объединении обоих).

0 голосов
/ 23 октября 2018

Вы не можете сопоставить шаблон таким образом, потому что Moving o полиморфен.Если у вас есть функция, которая перемещает только маркеры, Moving Bullet будет работать так же, как это.

Есть много разных способов обойти это. Одно простое решение, в зависимости от других аспектов вашей игры, может привести Bullet и Asteroid к одному типу данных Movable, для которого вместо этого вы можете сопоставить шаблон:

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