微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

monad 转换器的最佳实践:隐藏或不隐藏 'liftIO'

如何解决monad 转换器的最佳实践:隐藏或不隐藏 'liftIO'

在此之前,我会说我是一个新手 Haskell 程序员(多年来偶尔会修改它)但是当涉及到 OOO 和命令式编程时,我有几年的时间在柜台上。我目前正在学习如何使用 monad 并通过使用 monad 转换器将它们组合起来(假设我有正确的术语)。


虽然我能够将事物组合/链接在一起,但我发现很难对最佳方式和风格是什么以及如何最好地组合/编写这些交互建立一种直觉。

具体来说,我很想知道使用 Lift/liftIO 的最佳实践(或至少是您的实践)是什么以及两者之间的任何风格,以及是否有办法(和好处)在我找到它们时隐藏它们'嘈杂'。

下面是一个示例片段,我将其放在一起以说明我的意思:

consumeRenderStageGL' :: RenderStage -> StateT RenderStageContext IO ()
consumeRenderStageGL' r = do 
    pushDebugGroupGL (name r)
    liftIO $ consumePrologueGL ( prologue r )
    liftIO $ consumeEpilogueGL ( epilogue r )
    consumeStreamGL   ( stream   r )
    liftIO $ popDebugGroupGL

调用的一些函数使用了状态 monad :

pushDebugGroupGL :: String -> StateT RenderStageContext IO ()
pushDebugGroupGL tag = do
    currentDebugMessageID <- gets debugMessageID
    liftIO $ GL.pushDebugGroup GL.DebugSourceApplication (GL.DebugMessageID currentDebugMessageID) tag
    modify (\fc -> fc { debugMessageID = (currentDebugMessageID + 1) })

consumeStreamGL :: Stream -> StateT RenderStageContext IO ()
consumeStreamGL s = do 
    mapM_ consumetokenGL s
    logGLErrors

虽然大多数人并不生活在 IO 中(这意味着它们必须被解除):

consumePrologueGL :: Prologue -> IO ()
consumePrologueGL p = do
    colourClearFlag     <- setupAndReturnClearFlag GL.ColorBuffer   ( clearColour  p ) (\(Colour4 r g b a) -> GL.clearColor $= (GL.Color4 r g b a))
    depthClearFlag      <- setupAndReturnClearFlag GL.DepthBuffer   ( clearDepth   p ) (\d -> GL.clearDepthf $= d)
    stencilClearFlag    <- setupAndReturnClearFlag GL.StencilBuffer ( clearStencil p ) (\s -> GL.clearStencil $= fromIntegral s)
    GL.clear $ catMaybes [colourClearFlag,depthClearFlag,stencilClearFlag]
    logGLErrors
    where
        setupAndReturnClearFlag flag mValue function = case mValue of 
            nothing     -> return nothing
            Just value  -> (function value) >> return (Just flag)

我的问题是:有没有办法在 consumeRenderStageGL' 中隐藏 liftIO,更重要的是,这是一个好主意还是一个坏主意?强>

我能想到的隐藏/摆脱liftIO的一种方法是将我的consumePrologueGLconsumeEpilogueGL都带入我的状态monad中,但这似乎是错误的这些函数不需要(也不应该)与之交互;所有这一切只是为了减少代码噪音。

我能想到的另一个选择是简单地创建函数的提升版本并在 consumeRenderStageGL'调用它们 - 这将减少代码噪音,但在执行/评估中是相同的。 >

第三个选项,即我的 logGLErrors 的工作方式,是我使用了一个类型类,该类为 IO 和我的状态 monad 定义了一个实例。

我期待阅读您的意见、建议和做法。

提前致谢!

解决方法

有几个解决方案。一个常见的做法是让您的基本操作 MonadIO m => m … 而不是 IO …

consumePrologueGL :: (MonadIO m) => Prologue -> m ()
consumePrologueGL p = liftIO $ do
  …

然后您可以在 StateT RenderStageContext IO () 中使用它们而无需换行,因为 MonadIO m => MonadIO (StateT s m),当然还有 MonadIO IO 其中 liftIO 是身份函数。

您还可以使用 StateT 中的 MonadStatemtl 部分进行抽象,因此如果您在其上方/下方添加另一个转换器,则不会遇到从/to StateT

pushDebugGroupGL
  :: (MonadIO m,MonadState RenderStageContext m)
  => String -> m ()

通常,transformers 类型的具体堆栈很好,它只是为了方便包装您的所有基本操作,以便所有 lift 都在一个地方.

mtl 有助于从您的代码中完全消除 lift 噪音,并且在多态类型 m 中工作意味着您必须声明函数实际使用的效果,并且可以替换不同的实现用于测试的所有效果(MonadIO 除外)。如果您的效果类型很少,那么使用 monad 转换器作为这样的效果系统会很棒;如果您想要更细粒度或更灵活的东西,您将开始解决让人们达到代数效应的痛点。

还需要评估您是否需要 StateT 而不是 IO。通常,如果您在 IO 中,您不需要 StateT 提供的纯状态,因此您不妨使用 StateT MutableState IO 代替 ReaderT (IORef MutableState) IO

也可以将它(或它的 newtype 包装器)设为 MonadState MutableState 的实例,因此您的代码使用 get/put/{{1} } 甚至不需要改变:

modify

{-# Language GeneralizedNewtypeDeriving #-} import Data.Coerce (coerce) newtype MutT s m a = MutT { getMutT :: ReaderT (IORef s) m a } deriving ( Alternative,Applicative,Functor,Monad,MonadIO,MonadTrans ) evalMutT :: MutT s m a -> IORef s -> m a evalMutT = coerce instance (MonadIO m) => MonadState s (MutT s m) where state f = MutT $ do r <- ask liftIO $ do -- NB: possibly lazier than you want. (a,s) <- f <$> readIORef r a <$ writeIORef r s ReaderT 的这种组合是一种非常常见的设计模式。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?