记录选择器和类型无效 类

Invalid record selector and type classes

我有一个 class Movable 和多个实例化这个 class 的数据类型。我想为所有这些 classes 创建一个通用的移动函数,如下所示,但显然我的记录语法不正确,因为我收到以下错误:

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')}
   |             ^^^^^^^^

我尝试应用此 Whosebug 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

首先,作为,您可能根本不应该使用任何class来解决这个问题,而只是一个参数化记录。

A class 不像在 OO 中那样,它实际上定义了一个数据结构。它只是定义了一些可能使用实例类型的操作来给你一些东西,但这只是一个特例。这些值也可能是即时计算的,通常没有办法将它们 设置 为另一个值。如果你需要那个,那么这样的“getter方法”是不够的,你还需要一个“setter”。在现代 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)