haskell - 如何使用 StateT、ContT 和 ReaderT 创建 monad?
问题描述
如何创建一个使用 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
需要 kind * -> *
?...我淹没在类型中,需要解释。Pause
必须采取什么形式,一个功能?ContT 如何整合?最终,我计划将 Cont 用于控制结构。
解决方案
与MonadReader
and不同MonadState
,MonadCont
类型类只接受一个参数。由于该参数m
必须是 a Monad
,因此它必须具有 kind * -> *
。
在您的派生子句中,您MonadCont,
不希望MonadCont Pause
.
在回答后续问题时添加:
ContT
定义为:
newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }
请注意,r
在您的定义中的newtype M r
是作为 final ( 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 转换器。)
推荐阅读
- java - 类型参数“S”的推断类型“S”不在其范围内;
- google-sheets - 如何在谷歌表格中计算 ARGMIN 或 ARGMAX
- c - 使用 char 时,scanf() 不适用于多个值
- php - 如何从函数内部的foreach循环生成的最后一项中删除最后一个逗号
- javascript - 获取匹配的 LAN 播放器
- c++ - 在源文件中使用#ifdefs 处理依赖于平台的行为?
- hadoop - 查找用于 hadoop 文件的压缩编解码器
- mysql - Python MySQL 插入 unicode
- javascript - 在angularjs wetheen组件中编辑输入表单
- python-3.x - 获取超过 10 天未使用访问密钥的 iam 用户列表的 Python 脚本