gpt4 book ai didi

haskell - 使用带外数据编写 monad(也称为并行组合 monad)

转载 作者:行者123 更新时间:2023-12-02 17:21:17 28 4
gpt4 key购买 nike

我目前正在编写一个名为 GL 的 monad 包装 OpenGL,我希望能够查询计算以获取它可能需要的每个纹理的列表。

这个问题已经解决了吗?我在为 GL 编写 Monad 实例时遇到了很多麻烦。

这是我迄今为止尝试过的:

-- GL should be able to be inspected for its HashSet without running the computation.
newtype GL a = GL (S.HashSet String) (IO a)

instance Monad (GL a) where
return = GL S.empty . return -- Calls IO.return
(>>=) (GL textures action) f = -- What goes here?

但我是不是找错了树?它实际上并不能作为一个 monad 工作,因为我必须在运行它之前查询它。我应该用什么来代替?我真的很喜欢使用 do 表示法。

我认为这可以分解为:如何并行组合两个 monad,然后独立运行它们?

最佳答案

GL 类型的问题是“计算结果”a 依赖于 IO 操作,因此您无法实现可以计算最终纹理的 monad 实例HashSet 不运行 IO 操作。

正确的解决方案取决于您想要如何使用 GL monad 的详细信息,但假设您可以决定使用哪些纹理而不运行 IO 操作,那么您可以使用这样的类型

type GL a = WriterT (Set String) (Writer (IO ())) a

即您使用两个嵌套的 writer monad,一个用于纹理,另一个用于累积 IO 操作。生成的 monad 堆栈分两个阶段运行,您无需执行 IO 操作即可获得最终的纹理集。

不幸的是,Writer仅适用于幺半群,因此我们需要首先为IO ()定义一个Monoid实例。

{-# LANGUAGE FlexibleInstances #-}

import Data.Monoid

instance Monoid (IO ()) where
mempty = return ()
mappend = (>>)

现在,您可以编写一个函数来注册新纹理,如下所示:

addTexture :: String -> GL ()
addTexture = tell . S.singleton

另一个函数可以缓存稍后执行的 IO 操作

addIO :: IO () -> GL ()
addIO = lift . tell

这是一个用于运行 GL monad 的实用函数

runGL :: GL a -> (a, Set String, IO ())
runGL gl = let iow = runWriterT gl
((a, textures), io) = runWriter iow
in (a, textures, io)

这会重新调整一个包含三个元素的元组:计算的结果值、累积的纹理集和累积的 io 操作。请注意,此时元组中的 IO () 值仅描述了操作,尚未执行任何操作(例如绘图操作)。

我不确定这是否涵盖您的用例,但希望它能给您一些关于如何构建合适的 monad 堆栈的想法。如果您需要更多帮助,请提供一些有关您希望如何实际使用 GL monad 的示例。

这是我测试过的完整代码。请注意,我使用了 Set 类型而不是 HashSet,因为根据 hashmap 的文档库中,HashSet 名称已被弃用。

{-# LANGUAGE FlexibleInstances #-}

import Control.Monad.Writer
import Data.Monoid
import Data.HashSet (Set)
import qualified Data.HashSet as S

instance Monoid (IO ()) where
mempty = return ()
mappend = (>>)

type GL a = WriterT (Set String) (Writer (IO ())) a

addTexture :: String -> GL ()
addTexture = tell . S.singleton

addIO :: IO () -> GL ()
addIO = lift . tell

runGL :: GL a -> (a, Set String, IO ())
runGL gl = let iow = runWriterT gl
((a, textures), io) = runWriter iow
in (a, textures, io)

编辑:如果按照 dave4420 的建议将 IO 效果包装在新类型中,您还可以避免语言扩展。

import Control.Monad.Writer
import Data.Monoid
import Data.HashSet (Set)
import qualified Data.HashSet as S

newtype WrapIO = WrapIO { unwrapIO :: IO () }

instance Monoid WrapIO where
mempty = WrapIO $ return ()
WrapIO a `mappend` WrapIO b = WrapIO $ a >> b

type GL a = WriterT (Set String) (Writer WrapIO) a

addTexture :: String -> GL ()
addTexture = tell . S.singleton

addIO :: IO () -> GL ()
addIO = lift . tell . WrapIO

runGL :: GL a -> (a, Set String, IO ())
runGL gl = let iow = runWriterT gl
((a, textures), WrapIO io) = runWriter iow
in (a, textures, io)

关于haskell - 使用带外数据编写 monad(也称为并行组合 monad),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9223368/

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