【问题标题】:What is the name of this Monad Stack function?这个 Monad Stack 函数的名称是什么?
【发布时间】:2011-08-23 12:08:20
【问题描述】:

我在 State monad 中有一堆有状态的函数。在程序中的某个时间点需要一些 IO 操作,所以我将 IO 包装在 StateT 中,得到一对这样的类型:

mostfunctions :: State Sometype a
toplevel :: StateT Sometype IO a

为了简单起见,我不想将 IO 上下文传递给主函数集,并且我想避免将它们包装在 monad 堆栈类型中。但是为了从顶层函数调用它们,我需要类似于电梯的东西,但我并不是想从内部单子中提升一个值。相反,我想将 StateT monad 中的状态转换为 State monad 中的等价物。为此,我有以下内容:

wrapST :: (State Sometype a) -> StateT Sometype IO a
wrapST f = do s <- get
              let (r,s2) = runState f s 
              put s2
              return r

然后习惯于交错如下内容:

toplevel = do liftIO $ Some IO functions
              wrapST $ Some state mutations
              liftIO $ More IO functions
              ....

这似乎是一个相当明显的代码块,所以我想知道这个函数是否有一个标准名称,并且它已经在标准库的某个地方实现了?我试图使描述保持简单,但显然这延伸到将一个变压器从堆栈中拉出,将包装的值转换为变压器类型的表亲,跳过堆栈中下面的单子,然后将结果推回结束。

【问题讨论】:

  • 你不能使用type State = StateT Identity这个事实吗?将StateT Sometype m a 用于大量函数,这样您就可以在StateT IOState 中运行它们。

标签: haskell monads monad-transformers state-monad


【解决方案1】:

更直接地回答您的问题:hoist 函数以稍微通用的方式完全按照您的描述进行。用法示例:

import Control.Monad.State
import Data.Functor.Identity
import Control.Monad.Morph

foo :: State Int Integer
foo = put 1 >> return 1

bar :: StateT Int IO Integer
bar = hoist (return . runIdentity) foo

hoistMFunctor 类的一部分,其定义如下:

class MFunctor t where
  hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b

大多数 monad 转换器都有实例,但 ContT 没有。

【讨论】:

  • 谢谢,这实际上是我最初寻找的答案。
【解决方案2】:

重构您的代码以使用类型StateT SomeType m a 而不是State SomeType a 可能是个好主意,因为第一个与任意monad 堆栈兼容。如果你像这样改变它,你就不再需要一个函数wrapST,因为你可以直接调用有状态的函数。

好的。假设你有一个函数subOne :: Monad m =&gt; State Int Int

subOne = do a <- get
            put $ a - 1
            return a

现在,将所有类似函数的类型从State SomeType a 更改为StateT SomeType m a,保持m 不变。这样,您的函数可以在任何一元堆栈上运行。对于那些需要 IO 的函数,您可以指定底部的 monad 必须是 IO:

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a

现在,应该可以同时使用这两个功能了:

-- You could use me without IO as well!
subOne :: Monad m => StateT Int m ()
subOne = do a <- get
            put $ a - 1

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a

toZero :: StateT Int IO ()
toZero = do subOne     -- A really pure function
            printState -- function may perform IO
            a <- get
            when (a > 0) toZero

PS:我使用 GHC 7,一些库在中途更改,所以在 GHC 6 上可能会有点不同。

【讨论】:

  • 当我尝试它时它不起作用“无法匹配预期类型标识与推断类型 IO”。我无法在这些 cmets 中放置代码片段,您能否发布一个简单示例来说明您的意思?
  • 非常好,这是实现我所寻找的更简单的方法。它也回答了我最初的问题:我正在寻找的功能是变压器的标准部件。顺便说一句,subOne 的签名需要为 ghci 添加一个“Monad m =>”。
  • 对于printStatetoZero,允许任何monad 可能是好的,只要IO 在堆栈的底部,即printState :: MonadIO m =&gt; StateT Int m (),或者只是删除类型签名: )
  • @hammar:啊……忘了。
  • 旁注:在最新的 Haskell 平台中,State 是根据 StateT 定义的,我认为所有可以推广到 monad 转换器的 monad 也是如此。直到最近,情况并非如此。这不会改变答案的正确性:仍应使用 StateT,因为它更通用。
猜你喜欢
  • 1970-01-01
  • 2012-06-15
  • 2019-10-10
  • 1970-01-01
  • 2014-05-18
  • 2014-10-03
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
相关资源
最近更新 更多