haskell - 围绕 Streaming & Reader 构建一个 monad
问题描述
我正在尝试构建Monad
以下类型的实例:
data CmdY ρ η ω = CmdY (Reader ρ ((Stream (Of CmdSpec) η ω)))
包装中的哪里Stream
和Of
来自哪里。Streaming
streaming
我已经成功编写了Functor
Applicative
实例:
instance (Monad η) => Functor (CmdY ρ η) where
fmap :: (α -> β) -> CmdY ρ η α -> CmdY ρ η β
fmap f (CmdY a) = CmdY $ (fmap f) <$> a
instance (Monad η) => Applicative (CmdY ρ η) where
pure = CmdY . pure . return
(<*>) :: CmdY ρ η (α -> β) -> CmdY ρ η α -> CmdY ρ η β
(CmdY f) <*> (CmdY a) = let ff = (<*>) <$> f
in CmdY $ ff <*> a
但我正在兜圈子试图实现(>>=)
Monad 的绑定。
我有一个函数来评估 CmdY 并给我一个流和结果:
runCmdY :: (MonadIO η, MonadIO μ, MonadReader (ProcExecCtxt μ) ψ) =>
CmdY (ProcExecCtxt μ) η ω -> ψ (Stream (Of CmdSpec) η ω)
但是给定 form 的绑定a >>= f
, a 是 typeCmdY (ProcExecCtxt η) η α
而 f 是 type α -> CmdY (ProcExecCtxt η) η β
,我需要得到一些 α 类型的东西来喂给我的 f,但我没能到达那里。
ProcExecCtxt m
这是一个执行上下文;它提供了一种在 monad m 中评估 cmds 的方法。
我确定我遗漏了一些明显的东西(或者至少,我希望一旦我看到它就会很明显);但我会感谢任何指示。
谢谢,
解决方案
Reader ρ (Stream (Of CmdSpec) η ω)
真的只是
ρ -> Stream (Of CmdSpec) η ω
还有第三种拼写该类型的方法,在这种情况下可能更有意义:
ReaderT ρ (Stream (Of CmdSpec) η) ω
使用Control.Monad.Trans.Reader.ReaderT
(也由 导出Control.Monad.Reader
),定义的 monad 转换器
newtype ReaderT e m a = ReaderT
{ runReaderT :: e -> m a }
既然ReaderT e
是 monad 转换器,ReaderT e m
那么无论何时m
都是 monad。因此,它将免费提供您想要的所有实例,以及更多。确实,使用
{-# language GeneralizedNewtypeDeriving #-}
你可以写
newtype CmdY ρ η ω = CmdY
{ unCmdY :: ReaderT ρ (Stream (Of CmdSpec) η) ω }
deriving (Functor, Applicative, Monad
, Alternative, MonadPlus, MonadReader ρ)
或者,如果您愿意,可以通过 wrapping 和 unwrapping 手动定义实例CmdY
:
instance Monad η => Functor (CmdY ρ η) where
fmap f (CmdY m) = CmdY (fmap f m)
instance Monad η => Applicative (CmdY ρ η) where
pure = CmdY . pure
CmdY fs <*> CmdY xs = CmdY (fs <*> xs)
instance Monad η => Monad (CmdY ρ η) where
CmdY m >>= f = CmdY $ m >>= unCmdY . f
CmdY ρ
本身就是一个单子变压器:
import Control.Monad.Trans.Class
instance MonadTrans (CmdY ρ) where
lift = CmdY . lift . lift
推荐阅读
- graphql - Apollo Graphql:重命名模式以实现向后兼容性
- c# - 获取 XML 元素的“名称”
- sql - Oracle 选择匹配某个键的最大值
- traefik - Traefik 的默认路由器
- spring-boot - java.sql.SQLException: ORA-22835: 缓冲区太小,无法进行 CLOB 到 CHAR 或 BLOB 到 RAW 转换
- database - 在 YugabyteDB YSQL 中,我们如何跟踪每台服务器上的活动连接数?
- .net-core - 有没有办法从 IDestination 检索队列/主题名称?
- c# - Entity Framework 6 以编程方式更新数据库没有自动迁移
- c# - 由客户端 Web 浏览器触发的 iisexpress 崩溃。错误是“程序 iisexpress exe 已退出,代码 0xc0000005 访问冲突”
- javascript - 取消悬停图像后如何使悬停效果保持不变?