【问题标题】:Invalid record selector and type classes无效的记录选择器和类型类
【发布时间】:2018-10-21 03:10:57
【问题描述】:

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

【问题讨论】:

  • 好吧,您没有使用字段position 定义记录类型,所以这是预期的行为。您在这里将position 定义为某种“getter”,因此即使您可以以某种方式调用这样的“setter”,也没有多大意义。
  • @WillemVanOnsem 不明白,我说我定义了具有 Movable 实例的类(正如您现在可以在问题代码中看到的那样)。
  • Haskell 中的class 更像是其他编程语言中的“接口”。您在此处定义如下函数:position :: m -> Position,因此它将Movable“转换”为Position,而不是相反。
  • 但是这个问题看起来不像我链接的问题吗? @willemVanOnsem
  • 你可以定义一个像setPosition -> Position -> m -> m这样的“setter”,然后调用“setter”。

标签: haskell typeclass


【解决方案1】:

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

一个类不像 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)

【讨论】:

  • 这行得通,谢谢。正如您现在两次建议的那样,我将使用参数化方式。我也只想知道如何使用这种方法。
猜你喜欢
  • 2013-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多