围绕 Streaming & Reader 构建一个 monad

Building a monad around Streaming & Reader

我正在尝试构建以下类型的 Monad 实例:

data CmdY ρ η ω = CmdY (Reader ρ ((Stream (Of CmdSpec) η ω)))

其中 StreamOf 来自 streaming 包中的 Streaming

我已经成功编写了FunctorApplicative个实例:

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) η ω)

但是给定一个 a >>= f 形式的绑定,a 是 CmdY (ProcExecCtxt η) η α 类型,而 f 是 α -> CmdY (ProcExecCtxt η) η β 类型,我需要得到一些 α 类型的东西来喂我f,我没能到达那里。

ProcExecCtxt m 这里是一个执行上下文;它提供了一种在 monad m.

中评估 cmds 的方法

我确定我遗漏了一些明显的东西(或者至少,我希望一旦我看到它就会很明显);但我很感激任何指点。

谢谢,

Data.Functor.Compose 实现了您的 FunctorApplicative 实例。

ReaderT 为您实现了一个 Monad 实例。

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 是一个单子变换器,所以 ReaderT e m 是一个单子,只要 m 是。因此,它将免费提供您想要的所有实例,以及更多实例。的确,使用

{-# language GeneralizedNewtypeDeriving #-}

你可以写

newtype CmdY ρ η ω = CmdY
  { unCmdY :: ReaderT ρ (Stream (Of CmdSpec) η) ω }
  deriving (Functor, Applicative, Monad
           , Alternative, MonadPlus, MonadReader ρ)

或者,如果您愿意,可以通过包装和解包手动定义实例 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 ρ 本身就是一个 monad 转换器:

import Control.Monad.Trans.Class

instance MonadTrans (CmdY ρ) where
  lift = CmdY . lift . lift