【问题标题】:How to create a monad using StateT, ContT, and ReaderT?如何使用 Static、ContentT 和 Reader 创建 monad?
【发布时间】:2019-04-23 11:41:53
【问题描述】:

如何创建一个使用 State、Cont 和 Reader 转换器的 monad?我想阅读一个环境,并更新/使用状态。但是,我也想暂停/中断动作。例如,如果满足某个条件,则状态保持不变。

到目前为止,我有一个使用 ReaderT 和 StateT 的 monad,但我不知道如何包含 ContT:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Test where
-- monads
import   Data.Functor.Identity (Identity, runIdentity)
import   Control.Monad.State
import   Control.Monad.Reader
import   Control.Monad.Cont

-- reader environment
type In = Integer

-- cont: if true then pause, else continue 
type Pause = Bool

-- state environment:
newtype StateType = StateType { s :: Integer }

newtype M r = M {_unM :: ReaderT In (ContT Pause (StateT StateType Identity)) r}
  deriving ( Functor, Applicative, Monad
           , MonadReader In
           , MonadCont   Pause
           , MonadState  StateType
           )

-- run monadic action
runM :: In -> Pause -> StateType -> M r -> StateType
runM inp pause initial act
  = runIdentity             -- unwrap identity
  $ flip execStateT initial -- unwrap state
  $ flip runContT   pause   -- unwrap cont
  $ flip runReaderT inp     -- unwrap reader
  $ _unM act                -- unwrap action

这给出了错误:

* Expected kind `* -> *', but `Pause' has kind `*'
* In the first argument of `MonadCont', namely `Pause'
  In the newtype declaration for `M'
  |
24|         , MonadCont  Pause
  |

好的,但是为什么Pause 需要亲切的* -> *?...我被类型淹没了,需要解释。 Pause 必须采取什么形式,一个函数? ContT 如何整合?最终,我计划将 Cont 用于控制结构。

【问题讨论】:

  • 一些随机的 cmets:你“堆叠”monad 转换器的顺序有时(通常)很重要。我看不出来你说的对不对。无论如何,ContT PauseContT Bool,它看起来有点奇怪,因为它是 (a -> m Bool) -> m Bool,这可能是也可能不是你想要的。
  • @chi 在堆栈排序方面,我想“读取”一个整数,如果整数是奇数,则不会发生状态更新,如果偶数则将其添加到状态(累加器)。这就是为什么我将它们堆叠起来,ReaderT (ContT (StateT)))。可能不正确,一直在阅读文档以确认。关于ContT Bool,我认为这是我困惑的根源。我最初将其视为ReaderTStateT,它们分别使用InStateType(a -> m Bool) -> m Bool 是什么?似乎比 StateType 或 Reader 环境类型更抽象。 ContT如何使用runM中的功能?

标签: haskell monads monad-transformers continuations


【解决方案1】:

MonadReaderMonadState 不同,MonadCont 类型类takes only one parameter。由于该参数m 必须是Monad,因此它必须具有种类* -> *

在您的派生子句中,您需要MonadCont, 而不是MonadCont Pause

在回答后续问题时添加:

ContTis defined as:

newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }

请注意,newtype M r 定义中的 r 作为最终 (a) 参数传递给 ContT。插入变量,你有

ContT Bool (State StateType) a = ContT { 
    runContT :: (a -> State StateType Bool) -> (State StateType Bool)
  }

这提供了一个计算上下文,您可以在其中操作StateType,并使用定界延续。最终,您将构造一个ContT Bool (State StateType) Bool。然后您可以运行延续(使用evalContT),并返回到更简单的State StateType 上下文。 (实际上,您可以在程序的同一部分中解开所有 3 个 monad 转换器。)

【讨论】:

  • 非常感谢!你能解释一下ContT 的参数是什么意思吗?即在M r = {... Reader In (ContT (???) (StateT StateType Identity)) r }这一行。
  • 继续is definednewtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }m 是您的示例中的基础 Monad - (StateT StateType Identity))。 a 是当前计算阶段的值。这些 ma 对于每个 Monad Transformer 都是相同的。 rContT 最终运行/消除的类型,evalContT
猜你喜欢
  • 2014-01-16
  • 2014-11-14
  • 1970-01-01
  • 2017-06-22
  • 1970-01-01
  • 2023-04-05
  • 2021-01-18
  • 2017-10-23
  • 2018-03-26
相关资源
最近更新 更多