无效的记录选择器和类型类

时间:2018-10-20 18:41:36

标签: haskell typeclass

我有一个类Movable和实例该类的多种数据类型。我想像下面所做的那样为所有这些类创建一个通用的移动函数,但是显然我的记录语法不正确,因为出现以下错误:

src\Controller.hs:24:13: error:
    * `position' is not a record selector
    * In the expression: o {position = (x', y')}
      In an equation for `move':
          move o
            = o {position = (x', y')}
            where
                (x, y) = position o
                (vx, vy) = velocity o
                x' = x + vx
                y' = y + vy
   |
24 | move o = o {position = (x', y')}
   |             ^^^^^^^^

我尝试应用此StackOverflow answer,但没有使它起作用。 该如何解决?还是除了使用记录语法来解决此问题外,还有其他方法吗? 在这里您可以看到我的代码:

type Position = (Float, Float)
type Velocity = (Float, Float)

class Movable m where
    position :: m -> Position
    velocity :: m -> Velocity

data Player = Player { 
                playerBulletType :: Bullet,
                playerHealth :: Health,
                playerMaxVelocity :: MaxVelocity,
                playerVelocity :: Velocity,
                playerPosition :: Position,
                playerSprite :: Sprite
              }

instance Movable Player where
    position = playerPosition
    velocity = playerVelocity

move :: Movable o => o -> o
move o = o {position = (x', y')}
  where (x, y) = position o
        (vx, vy) = velocity o
        x' = x + vx
        y' = y + vy

1 个答案:

答案 0 :(得分:4)

首先,作为we already recommended in the other question,您可能根本不应该使用任何类来解决此问题,而应该仅使用参数化记录。

类与OO中的类不同,它实际上定义了数据结构。它只是定义了一些操作,这些操作可能使用实例类型的为您提供某些东西,但这只是一个特例。这些值也可能是即时计算的,通常没有办法将它们设置为另一个值。如果需要,那么这种“获取方法”是不够的,您还需要“设定者”。在现代的Haskell中,习惯上,您会一举两得:将getter和setter的组合称为lens

import Control.Lens

class Movable m where
  position :: Lens' m Position
  velocity :: Lens' m Velocity

instance Movable Player where
  position f plyr = fmap (\p -> plyr{playerPosition=p}) . f $ playerPosition plyr
  velocity f plyr = fmap (\v -> plyr{playerVelocity=v}) . f $ playerVelocity plyr

那你就可以写

move :: Movable o => o -> o
move o = o & position .~ (x', y')
  where (x, y) = o ^. position
        (vx, vy) = o ^. velocity
        x' = x + vx
        y' = y + vy

或更短vector-space

import Data.AffineSpace

move :: Movable o => o -> o
move o = o & position %~ (.+^ o^.velocity)