实现 Reader monad(来自 Real World Haskell 一书)

Implementing the Reader monad (from Real World Haskell book)

newtype Reader e a = R { runReader :: e -> a }

instance Monad (Reader e) where 
  return a = R $ \_ -> a
  m >>= k  = R $ \r -> runReader (k (runReader m r)) r

我很难理解这两个片段。我可以看出第一个是 reader 的记录语法描述,它具有从 ea 的函数 runReader,但第二个让我感到困惑。

通过将 mk 绑定,它实际上是在尝试创建一个新的 Reader,但是

runReader (k (runReader m r)) r

锻炼身体?我以为 runReader 只需要一个参数,但现在似乎需要两个参数,一个是 k (runReader m r) 另一个是 r.

提前致谢。

I am having difficulty understanding the Reader Monad.

编辑:我应该指出一些关于这方面的资源,而不是试图重复它们。

I thought runReader only takes one argument, but it seems it's taking two right now, one being k (runReader m r) and another being r.

如果您查看 runReader :: Reader e a -> e -> a 的类型签名,这可以被视为采用 Reader e a 并生成 e -> a,或者采用 Reader e a和一个 e 并产生一个 a。 reader monad 的要点是引入一个隐式参数。

How does runReader (k (runReader m r)) r work out?

您可以详细说明 bind 运算符定义:

instance Monad (Reader e) where 
  return a = R $ \_ -> a
  -- (>>=) :: Reader e a -> (a -> Reader e b) -> Reader e b
  ma >>= k = R $ \e -> let a = runReader ma e
                           mb = k a
                       in runReader mb e

即首先“ma是运行和e”(runReader ma :: e -> a应用于e)。这会产生一个 a.

那么k a就是运行。这会产生一个 mb.

那么“mb 是 运行 和 e”(runReader mb :: e -> b 应用于 e)。

这被打包成 R $ \e -> ... runReader mb e

我认为理解这一点的困难部分主要与 newtype 需要不断包装 (R) 和展开 (runReader) 的内容,而不是臭名昭著的 monads 的工作原理.


想象一下,您唯一需要的 monad 是 reader monad,我们可以不用 newtypeinstance Monad (Reader e) 绒毛。那么您的定义可能如下所示:

type Reader e a = e -> a
-- type Reader e a = (->) e a
-- type Reader e = (->) e

unit :: a -> Reader e a
--   :: a -> (e -> a)
unit a = \_e -> a
-- unit a _e = a
-- unit = const

ask :: Reader e e
--  :: e -> e
ask = \e -> e
-- ask e = e
-- ask = id

bind :: Reader e a -> (a -> Reader e b) -> Reader e b
--   :: (e -> a)   -> (a -> (e -> b))   -> (e -> b)
bind ma k = \e -> let a = ma e
                      mb = k a
                  in mb e
-- bind ma k e = let mb = k (ma e) in mb e

在这一点上变得更加清楚,所有 unit 所做的就是丢弃 e,所有 ask 所做的就是 return e,以及什么 bind 做的是采用两个函数(mak (ma e),又名 mb),它们都需要一个 e,将它们组合在一个新函数中,该函数也需要一个 e,而不必在组合过程中显式传递 e

我在学习如何编写 monad 定义时有一个误解是 runReader 运行s 任何东西。它帮助我在概念上将其称为 unR,因为它所做的只是删除 R 包装。