gpt4 book ai didi

haskell - 将计算从 State monad 提升到 RWS monad

转载 作者:行者123 更新时间:2023-12-02 15:18:03 24 4
gpt4 key购买 nike

我围绕 RWS (Reader+Writer+State) monad 的使用构建了一个计算:

newtype Problem a = Problem { unProblem :: RWS MyEnv MyLog MyState a }
deriving ({- lots of typeclasses -})

计算是通过组装形式的基本计算逐步构建的

foo :: a -> Problem b

然而,有时子计算不需要RWS monad 的全部功能。例如,考虑

bar :: c -> State MyState d

我想在 Problem monad 的上下文中使用 bar 作为更大计算的一部分。我可以看到执行此操作的三种方法,但对我来说都不是很优雅。

  1. 手动解包 State 计算并将其重新包装在 RWS monad 中:

    baz :: a -> RWS MyEnv MyLog MyState c
    baz x = do temp <- foo x
    initialState <- get
    let (finalResult, finalState) = runState (bar temp) initialState
    put finalState
    return finalResult
  2. 修改 bar 的类型签名,将其提升到 Problem monad 中。这有一个缺点,即新类型签名没有明确 promise bar 独立于 MyEnv 并且不向 MyLog 记录任何内容。

  3. 用显式 ReaderT MyEnv WriterT MyLog State MyState monad 堆栈替换 RWS monad。这让我可以简洁地 lift.lift bar 子计算到完整的 monad 中;但是,这个技巧将不起作用,例如用于 c -> Reader MyEnv d 形式的子计算。

有没有更简洁的方法来组合 foobar?我有一种预感,类型类实例的一些巧妙定义可能会起作用,但我看不出具体如何进行。

最佳答案

我假设您使用的是 mtl (如果你不是,请考虑这样做 - 除了以下内容之外,这些库大多是兼容的)。您可以派生 MonadReader MyEnvMonadWriter MyLogMonadState MyState 的实例。然后,您可以使用它们在具有此类约束的任何 monad 堆栈上泛化您的函数。

{-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses, FlexibleContexts #-}

import Control.Monad.RWS
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State

newtype Problem a = Problem { unProblem :: RWS MyEnv MyLog MyState a }
deriving (Functor, Applicative, Monad,
MonadReader MyEnv, MonadWriter MyLog, MonadState MyState)

从你的例子来看,也许bar只需要知道有一些状态MyState,所以你可以给它签名

bar :: MonadState MyState m => c -> m d
bar = ...

然后,即使对于可能需要完整的RWS 功能的foo,您也可以编写

foo :: (MonadState MyState m, MonadReader MyEnv m, MonadWriter MyLog m) => a -> m b
foo = ...

然后,您可以根据自己的喜好混合搭配:

baz :: Problem ()
baz = foo 2 >> bar "hi" >> return ()

现在,为什么这通常有用?它归结为您的 monad“堆栈”的灵 active 。如果明天你决定你实际上不需要 RWS 而只需要 State,你可以相应地重构 Problembar(一开始只需要状态)将继续工作而无需任何更改。

一般来说,我尽量避免在foo等辅助函数中使用WriterTStateTRWST等或 bar。选择使用类型类 MonadWriterMonadStateMonadReader 等,尽可能让您的代码保持通用和独立于实现。然后,您只需必须在代码中使用一次 WriterTStateTRWST:当您真正“运行”您的 monad 时。

关于变形金刚的旁注

如果您使用 transformers ,这些都不起作用。这不一定是坏事:mtl,因为它始终能够“找到”组件(如状态或编写器)has some problems .

关于haskell - 将计算从 State monad 提升到 RWS monad,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39195846/

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