简体   繁体   English

理解 Reader monad

[英]Understanding the Reader monad

I'm reading PureScript by Example and got to the part introducing the Reader monad.我正在阅读PureScript by Example并阅读了介绍 Reader monad 的部分。 The example goes like this:这个例子是这样的:

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

The confusing part for me is the ask function. The signature is:令我困惑的部分是ask function。签名是:

ask   :: forall r. Reader r r

It appears as if it creates a Reader out of thin air看起来好像它凭空创建了一个阅读器

When I was reading about the State monad, it had the same concept with its get function. And the text explained:当我阅读State monad 时,它与get function 具有相同的概念。文本解释如下:

the state is implemented as a function argument hidden by the State monad's data constructor, so there is no explicit reference to pass around. state 实现为 function 参数,隐藏在 State monad 的数据构造函数中,因此没有显式引用可传递。

I'm guessing this is the key, and the same thing is happening here with the Reader, but I don't understand how it works...我猜这是关键,Reader 也发生了同样的事情,但我不明白它是如何工作的......

When the above example is run via runReader , how does the provided value suddenly appear as a result of ask ?当上面的示例通过runReader运行时,提供的值是如何突然作为ask的结果出现的? The Haskell docs for ask say: Retrieves the monad environment. ask的 Haskell 文档说:检索 monad 环境。 But my confusion is from where ?但我的困惑是从哪里来的 The way I see it, a value gets passed to runReader , gets stored somewhere , and to get it - you call ask ... but that makes no sense.在我看来,一个值被传递给runReader ,被存储在某个地方,然后得到它——你调用ask ……但这没有任何意义。

While the example is PureScript, I'm guessing any Haskell-literate person would also be able to answer, hence the Haskell tag.虽然这个例子是 PureScript,但我猜任何懂 Haskell 的人也能回答,因此是 Haskell 标签。

I don't have a PureScript environment around currently, so I'll try to answer from a Haskell perspective, and hope it helps.我目前没有 PureScript 环境,所以我会尝试从 Haskell 的角度来回答,希望它有所帮助。

A Reader is really only a 'wrapper' around a function, so when you get a Reader rr , you really only get a reader from r to r ; Reader实际上只是一个函数的“包装器”,因此当您获得Reader rr ,您实际上只能获得从rr的阅读器; in other words, a function r -> r .换句话说,一个函数r -> r

You can summon functions out of thin air, because, if you're a Platonist, I suppose they always exist...可以凭空召唤函数,因为,如果你是柏拉图主义者,我想它们总是存在的……

When you use do notation, you're 'inside the monad', so the context r is implicit.当您使用do表示法时,您处于“monad 内部”,因此上下文r是隐式的。 In other words, you call a function that returns the r value, and when you use the <- arrow, you simply get that context.换句话说,您调用一个返回r值的函数,当您使用<-箭头时,您只需获取该上下文。

You can convince yourself that it works by performing a few substitutions.您可以通过执行一些替换来说服自己它有效。 First look at the signature of createUser .首先看createUser的签名。 Let's "unroll" the definition of Reader :让我们“展开” Reader的定义:

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

The ReaderT type only has one data constructor: ReaderT (r -> ma) , which means createUser is a term that evaluates to a value of type ReaderT (Permissions -> Identity (Maybe User)) . ReaderT类型只有一个数据构造函数: ReaderT (r -> ma) ,这意味着createUser是一个计算为ReaderT (Permissions -> Identity (Maybe User))类型值的术语。 As you can see, it is just a function tagged with ReaderT .如您所见,它只是一个带有ReaderT标记的函数。 It does not have to create anything out of thin air, but will receive the value of type Permissions when that function is called.它不必凭空创建任何东西,但会在调用该函数时收到Permissions类型的值。

Now let's look at the line you are having trouble with.现在让我们看看您遇到问题的线路。 You know that the do notation is just syntactic sugar, and the expression:你知道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

To understand what this does, you will have to lookup the definition of ask , >>= and pure for ReaderT .要了解它的作用,您必须查找ask>>=pure for ReaderT Let's perform another round of substitutions:让我们进行另一轮替换:

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

As you can see, createUser is clearly just a function wrapped by ReaderT that threads a value (the "environment") through your expressions.如您所见, createUser显然只是一个由ReaderT包装的函数,它通过您的表达式线程化一个值(“环境”)。 runReader unwraps the function and calls it with the provided argument: runReader包函数并使用提供的参数调用它:

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

The partial function type (->) r is a functor ie r->a is a container for any type a (a List a of size two is equivalent to a function Bool->a ).部分 function 类型(->) r是一个函子,即r->a是任何类型a的容器(大小为 2 的List a相当于 function Bool- Bool->a )。 Moreover, it is also a monad此外,它也是一个单子

instance Monad ((->) r) where
    f >>= k = \ r -> k (f r) r

It satisfies the MonadReader type class and is called the simple reader monad and might be given the type synonym Reader r .它满足MonadReader类型 class 并被称为简单阅读器 monad 并且可能被赋予类型同义词Reader r

ask returns this monad (->) r) r applied to the same type r , which we then can bind. ask返回此 monad (->) r) r应用于同一类型r ,然后我们可以绑定它。

Understand the partial function type (->) r better.更好地理解部分 function 类型(->) r

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM