Function 哈斯凯尔:“这是一个很好的例子。”;“多少钱?”;函数应该接收的类型?并避免“完全的”;重建“;

Function 哈斯凯尔:“这是一个很好的例子。”;“多少钱?”;函数应该接收的类型?并避免“完全的”;重建“;,function,haskell,types,Function,Haskell,Types,我有以下数据类型: data PointPlus = PointPlus { coords :: Point , velocity :: Vector } deriving (Eq) data BodyGeo = BodyGeo { pointPlus :: PointPlus , size :: Point } deriving (Eq) data Body = Body { geo :: BodyGeo , pict ::

我有以下数据类型:

data PointPlus = PointPlus
    { coords :: Point
    , velocity :: Vector
    } deriving (Eq)

data BodyGeo = BodyGeo
    { pointPlus :: PointPlus
    , size :: Point
    } deriving (Eq)

data Body = Body
    { geo :: BodyGeo
    , pict :: Color
    } deriving (Eq)
它是我游戏中角色、敌人、物体等的基本数据类型(我现在只有两个矩形作为玩家和地面:p)

当按下一个键时,字符会向右、向左移动或通过更改其
速度进行跳跃。移动是通过将
速度
添加到
坐标
来完成的。目前,其内容如下:

move (PointPlus (x, y) (xi, yi)) = PointPlus (x + xi, y + yi) (xi, yi)
我只是取我的
正文的
部分,而不是整个
正文,否则它将是:

move (Body (BodyGeo (PointPlus (x, y) (xi, yi)) wh) col) = (Body (BodyGeo (PointPlus (x + xi, y + yi) (xi, yi)) wh) col)
move
的第一个版本更好吗?无论如何,如果
move
仅更改
PointPlus
,则必须有另一个函数在新的
正文中调用它。我解释一下:有一个函数
update
,用来更新游戏状态;它被传递当前游戏状态,一个单独的
主体
,并返回更新后的
主体

update (Body (BodyGeo (PointPlus xy (xi, yi)) wh) pict) = (Body (BodyGeo (move (PointPlus xy (xi, yi))) wh) pict)
这让我发痒。除了
PointPlus
之外,
正文中的所有内容都保持不变。有没有办法避免这种完全的手工“重建”?例如:

update body = backInBody $ move $ pointPlus body
当然,不必定义
背部

您正在寻找“镜头”。镜片有几种不同的包装;这是对它们的一个很好的总结

我的理解是,对于某些字段
b
,数据类型
a
上的镜头提供了两种操作:一种是获取
b
值的方法,另一种是获取具有不同
b
值的新
a
。因此,您只需使用镜头来处理深度嵌套的
PointPlus

镜头包提供了使用镜头的有用功能,以及自动生成镜头的方法(使用模板Haskell),这可能非常方便


我认为它们对于您的项目是值得研究的,特别是因为由于数据类型的结构,您可能会在其他地方遇到类似的嵌套问题。

这太完美了,尤其是在自动生成的情况下!移动
功能如何?它是把整个
身体
,还是只把它的
PointPlus
部分拿出来比较好?@L01man我在我的一篇关于镜头的博客文章中也描述了一个与你非常相似的具体例子。你的逐步解释帮助我完全理解镜头。你不认为Haskell应该默认实现镜头吗?类型是语言的一部分,记录语法与镜头有点冗余,但功能不太强大。