我有一个类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
答案 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)