gpt4 book ai didi

haskell - 如何将 IO 操作的结果注入(inject)到非 IO 单子(monad)计算中

转载 作者:行者123 更新时间:2023-12-04 01:54:13 25 4
gpt4 key购买 nike

我有一点架构问题,我想看看是否有一个通用模式或抽象可以帮助我。我是 writing a game engine用户可以将游戏循环指定为以下形式的一元计算:

gameLoop :: TimeStep -> a -> Game a
Game monad 有很多用于绘制、转换和与引擎交互的访问点。然后,我还提供了一个用户调用来运行模拟的函数
runGame :: (TimeStep -> a -> Game a) -> a -> IO a

库的主要设计目标之一是不制作 Game MonadIO 的一个实例类型类。这是为了防止用户通过更改底层图形调用的状态或在不期望的情况下加载东西来防止用户自责。但是,经常有 IO a 的结果的用例。在游戏循环已经开始后很有用。特别是,我想到了用程序生成的图形元素生成敌人。

因此,我想允许用户使用类似于以下界面的东西来请求资源:
data ResourceRequestResult a
= NotLoaded
| Loaded a

newtype ResourceRequest a = ResourceRequest {
getRequestResult :: Game (ResourceRequestResult a)
}

requestResource :: IO a -> Game (ResourceRequest a)

有了这个,我想 fork 一个线程来加载资源并将结果传递给 Game 的上下文。 monad 并返回给用户。主要目标是让我决定 IO Action 何时发生——我期望它发生的地方,而不是在游戏循环的中间。

我想到的一个想法是在 Game 之上放置另一个用户定义的 monad 转换器。单子(monad)......类似的东西
newtype ResourceT r m a = ResourceT (StateT [ResourceRequest r] m a)

但是,我相信然后根据 f :: ResourceT r Game a 指定事物成为 API 的噩梦,因为我必须支持任何可能的 monad 转换器堆栈组合。理想情况下,我也想避免制作 Game r 中的多态,因为它会增加底层 Game 的冗长性和可移植性功能也一样。

Haskell 对这种编程模式有任何抽象或习语吗?我想要的不可能吗?

最佳答案

最简单的就是使用模块级封装。像这样的东西:

module Game (Game, loadResource) where

data GameState -- = ...
newtype Game = Game { runGame :: StateT GameState IO a }

io :: IO a -> Game a
io = Game . liftIO

loadResource :: IO a -> Game (Game a)
loadResource action = io $ do
v <- newEmptyMVar
forkIO (action >>= putMVar v)
return . io $ takeMVar v

如此处所示,您可以使用 Game可以做 IOGame 内模块而不向世界其他地方公开这个事实,只公开 IO 的位你认为“安全”的。特别是,您不会制作 Game MonadIO 的一个实例(并且它不能作为 MonadTrans 的实例,因为它的类型错误)。此外, io功能和 Game构造函数没有被导出,所以用户不能以这种方式拉一个结束运行。

关于haskell - 如何将 IO 操作的结果注入(inject)到非 IO 单子(monad)计算中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27391293/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com