令人困惑的 ReaderT 定义

Gia*_*eri 4 monads haskell monad-transformers reader-monad

作为练习,我一直在重新实现一些常见的 monad 及其相应的转换器;以下是我定义的一些类型:

newtype Writer  w   a = Writer  { runWriter  :: (w, a) }
newtype WriterT w m a = WriterT { runWriterT :: m (Writer w a) }

newtype Maybe    a = Just a | Nothing
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
Run Code Online (Sandbox Code Playgroud)

据我所知,变压器将一个单子“包裹”在一个外部单子中m;遵循这种直觉,我尝试ReaderT以类似的方式定义变压器:

newtype Reader  r   a = Reader  { runReader  :: r -> a }
newtype ReaderT r m a = ReaderT { runReaderT :: m (Reader r a) }
Run Code Online (Sandbox Code Playgroud)

然而,我在实现它的 monad 实例时遇到了困难。通过查看库定义,ReaderT我发现它被定义为

newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
Run Code Online (Sandbox Code Playgroud)

Reader除了它不使用(可以用 定义)这一事实之外ReaderT,为什么它被定义为r -> m a和 not m (r -> a)?我认为它应该用外部单子来包裹内部Reader(即)而不是r -> amm (r -> a)r -> m a

Die*_*Epp 6

m (r -> a)意味着您:

\n
    \n
  1. 首先在 monad 中执行一些操作,然后,

    \n
  2. \n
  3. 动作完成后后,对reader值进行纯计算。

    \n
  4. \n
\n

但你需要能够使用ask

\n
ask :: Monad m => ReaderT r m r\n
Run Code Online (Sandbox Code Playgroud)\n

ask,您可以:

\n
    \n
  1. 首先读取值,

    \n
  2. \n
  3. 然后执行一些单子操作,作用于该值。

    \n
  4. \n
\n

这就是为什么r -> m a有道理并且m (r -> a)没有道理的原因。

\n

如果有帮助,请将其视为 \xe2\x80\x9csymmatical\xe2\x80\x9d 和 writer monad。对于 writer monad,monad 位于外部,结果位于内部。对于阅读器 monad,输入位于外部,而 monad 位于内部。这种对称性经常出现。

\n