理解Reader monad

时间:2017-10-13 21:20:30

标签: haskell monads purescript state-monad reader-monad

我通过示例阅读 Purescript 并进入了介绍Reader monad的部分。这个例子是这样的:

createUser :: Reader Permissions (Maybe User)
createUser = do
  permissions <- ask
  if hasPermission "admin" permissions
    then map Just newUser
    else pure Nothing

令我困惑的部分是ask功能。签名是:

ask   :: forall r. Reader r r

看起来好像是凭空创造了一个阅读器

当我阅读State monad时,它与get函数具有相同的概念。文字解释说:

状态被实现为State monad的数据构造函数隐藏的函数参数,因此没有明确的引用传递。

我猜这是关键,读者也会发生同样的事情,但我不明白它是如何运作的......

当上述示例通过runReader运行时,ask会突然显示所提供的值? ask的Haskell文档说:检索monad环境。但我的混淆是从哪里?我看到它的方式,一个值传递给runReader,存储到某个地方,然后得到它 - 你调用ask ......但这没有任何意义。< / p>

虽然示例是Purescript,但我猜测任何有Haskell文化的人也能够回答,因此Haskell标记。

2 个答案:

答案 0 :(得分:10)

我目前没有PureScript环境,因此我将尝试从Haskell的角度回答,并希望它有所帮助。

Reader 实际上只是一个&#39;包装器&#39;围绕一个函数,所以当你得到一个Reader r r时,你真的只能得到一个从rr的读者;换句话说,一个函数r -> r

可以凭空消灭功能,因为如果你是柏拉图主义者,我想他们总是存在......

当您使用do符号时,您已经在monad&#39;内部,因此上下文r是隐含的。换句话说,您调用一个返回r值的函数,当您使用<-箭头时,您只需获取该上下文。

答案 1 :(得分:2)

你可以通过执行一些替换来说服自己这样做。首先看一下createUser的签名。让我们“展开”Reader的定义:

createUser :: Reader Permissions (Maybe User)
{- definition of Reader -}
createUser :: ReaderT Permissions Identity (Maybe User)

ReaderT类型只有一个数据构造函数:ReaderT (r -> m a),这意味着createUser是一个计算为ReaderT (Permissions -> Identity (Maybe User))类型值的术语。如您所见,它只是一个标有ReaderT的函数。它不必凭空创建任何内容,但在调用该函数时将获得类型Permissions的值。

现在让我们来看看你遇到麻烦的那条线。你知道do表示法只是语法糖,表达式是:

do permissions <- ask
   if hasPermission "admin" permissions
     then map Just newUser
     else pure Nothing

desugars to

ask >>= \permissions -> 
  if hasPermission "admin" permissions
  then map Just newUser
  else pure Nothing

要了解这一点,您必须为ask查找>>=pureReaderT的定义。让我们进行另一轮替换:

ask >>= \permissions -> ...
{- definition of ask for ReaderT -}
ReaderT pure >>= \permissions -> ...
{- definition of >>= for ReaderT -}
ReaderT \r ->
  pure r >>= \a -> case (\permissions -> ...) a of ReaderT f -> f r
{- function application -}
ReaderT \r ->
  pure r >>= \a -> 
    case (if hasPermission "admin" a
          then map Just newUser
          else pure Nothing) of ReaderT f -> f r
{- definition of pure for Identity -}
ReaderT \r ->
  Identity r >>= \a -> 
    case (if hasPermission "admin" a
          then map Just newUser
          else pure Nothing) of ReaderT f -> f r
{- definition of >>= for Identity -}
ReaderT \r ->
  (\a -> 
    case (if hasPermission "admin" a
          then map Just newUser
          else pure Nothing) of ReaderT f -> f r) r
{- function application -}
ReaderT \r ->
  case (if hasPermission "admin" r
        then map Just newUser
        else pure Nothing) of ReaderT f -> f r

正如您所看到的,createUser显然只是由ReaderT包裹的函数,它通过表达式对值(“环境”)进行线程化。 runReader解包该函数并使用提供的参数调用它:

runReader :: forall r a. Reader r a -> r -> a
runReader (ReaderT f) r = f r