问题
我正在尝试用 PureScript 编写游戏引擎。我是新手,但是自从我之前经历过 Real World Haskell 以来,学习一直很顺利(尽管我也没有太多将 Haskell 用于“真实”事物的经验)。任何将我的运行时错误尽可能多地转化为编译时错误的事情,在我的书中都是一个胜利——但如果该语言被证明过于限制我抽象问题的能力,它可以消除一些胜利。
好的,所以,我正在尝试在 HTML5 Canvas/context2d 上用 PureScript 构建一个 2d 游戏引擎(显然 purescript-canvas 是一个很好的选择 - 我更喜欢它而不是 Elm 的 Graphics.Canvas 模块,因为它映射了很多更接近实际的底层 JS API,特别是让我可以访问 Canvas 的各个像素)。
在我现有的(未完成但可用的)JS 引擎中,核心功能是我将保留一个“精灵”列表(异构,除了它们都共享一个公共类),并遍历它们以调用.update(timeDelta)
和.draw(context2d)
方法。
这些精灵都共享一个通用接口,但必须在底层支持根本不同的数据。一个可能有 x/y 坐标;另一个(可能代表环境效果)可能具有“完成百分比”或其他动画状态。
问题是,我只是想不出一个等效的抽象(异构/共享类列表)来做我需要它做的事情,而不滥用 FFI 来破解我的方式进入非常不纯的代码。
解决方案(及其问题)
异构列表 (duh)
显然,可以做异构列表等效的最佳可能抽象是异构列表。
哈斯克尔风格
事实证明,Haskell(即欺骗的 GHC,而不是官方规范/报告)提供了我想要的- 你可以在仍然保持类约束的同时将类型信息装箱,在列表中的所有项目上应用单个多态函数,不破坏类型安全。这将是理想的,但可惜 PureScript 目前不允许我表达如下类型:
data ShowBox = forall s. Show s => SB s
PureScript-Style(最先进的)
对于 PureScript,有purescript-exists包,它可能旨在提供与上面的 Haskell 解决方案等效的功能,并让我(不是隐藏,而是删除)类型信息,然后将其重新放入。这会让我有一个异构列表,但会以完全破坏类型安全为代价。
更重要的是,我认为我不能让它令我满意,因为即使我有一个 的列表[Exists f]
,我也不能只是将类型提取/重新添加为泛型forall a. (Draw a) => a
——我必须知道实际的我正在恢复的类型。我可以包含某种“标签”,告诉我应该提取哪种“真实”类型,但如果我要使用这些恶作剧,我还不如用纯 JS 编码。我可能必须做的(对于列表,不一定是包含的精灵)。
一个海量数据价值中的所有状态
我可以将所有精灵统一为具有相同类型,通过在一个庞大的结构中表示各个精灵的所有状态,将其传递给每个精灵的“更新”实现(仍然不能使用类多态性,但我可以包含一个突变作为类型的一部分的每个单独的精灵值的函数,并使用它)。这很糟糕,原因很明显:每个精灵都可以自由地改变/更新其他精灵的数据。对于我必须表示的每种新的精灵状态,必须在全局范围内更新海量数据结构。无法制作它的库,因为每个使用引擎的人都必须修改它。还不如是JS。
分离的同质状态类型
或者每个精灵可以有单独的状态,并且都具有相同的状态表示。这将避免“手指在彼此的馅饼中”的情况,但我仍然有一个统一的结构,我必须通过对每个精灵的需求的过多了解来更新,对于不需要的类型结构的那些位,大量浪费的数据由每个精灵。非常糟糕的抽象。
用 JSON 表示不同的数据或者你有什么
嗯。这种方式基本上只是使用 JS 数据并假装它是 PureScript。不得不抛弃 PureScript 打字的所有优势。
没有抽象
我可以将它们都视为完全不相关的类型。这意味着如果我想添加一个新的精灵,我必须更新最外层的draw
函数来为最外层的函数添加一个drawThisParticularSprite
, 同上update
。可能是所有可能解决方案中最糟糕的一个。
我可能会做什么
假设我对可用的抽象选择的评估是正确的,很明显我将不得不以一种或另一种方式滥用 FFI 来做我需要的事情。也许我会有一个统一的记录类型,比如
type Sprite = { data: Data, draw: Data -> DrawEffect, update: Data -> Data }
哪里Data
有一些杂乱无章的类型删除的东西,比如某种类型的东西Exists f
,和
type DrawEffect = forall e. Eff (canvas :: Canvas | e) Context2D
或者其他的东西。和方法都特定于单个记录,并且都“知道”要从中提取的真实draw
类型。update
Data
与此同时,我可能会继续询问 PureScript 开发人员是否有可能支持 Haskell 风格的存在主义东西,这样我就可以在不破坏类型安全的情况下获得一个正确的、真正的异构列表。我认为主要的一点是(对于之前链接的 Haskell 示例),ShowBox
必须存储其(隐藏)成员的实例信息,因此它会Show
从自己的show
函数覆盖中知道要使用的正确实例.
恳求
有人可以确认以上关于我目前在 PureScript 中的可用选项是否准确?我会很感激任何更正,特别是如果你发现了一种更好的方法来处理这个问题——特别是如果有一种方法允许我只使用“纯”代码而不牺牲抽象——请告诉我!