首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >解读“读者群”

解读“读者群”
EN

Stack Overflow用户
提问于 2017-10-13 21:20:29
回答 3查看 1.6K关注 0票数 13

我正在通过例子阅读PureScript,并了解了介绍Reader的部分。例子是这样的:

代码语言:javascript
复制
createUser :: Reader Permissions (Maybe User)
createUser = do
  permissions <- ask
  if hasPermission "admin" permissions
    then map Just newUser
    else pure Nothing

对我来说,令人困惑的是ask函数。签名是:

代码语言:javascript
复制
ask   :: forall r. Reader r r

它看起来像是在稀薄的空气中创建一个读取器

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

状态是作为由state monad的数据构造函数隐藏的函数参数实现的,因此没有明确的引用可传递。

我猜这是关键,同样的事情也发生在阅读器上,但我不明白它是如何工作的.

当上面的示例通过runReader运行时,提供的值如何突然显示为ask的结果?ask的Haskell文档say:检索monad环境。但我的困惑是从哪里来的?在我看来,一个值被传递给runReader,存储在某个地方,为了得到它--你叫ask.但这毫无意义。

虽然这个例子是PureScript,但我猜任何一个懂Haskell的人也能回答,因此就使用了Haskell标签。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-10-13 21:37:55

我目前还没有一个PureScript环境,所以我将尝试从Haskell的角度来回答,并希望它能有所帮助。

阅读器实际上只是一个函数的“包装器”,所以当您得到一个Reader r r时,您实际上只能从rr得到一个阅读器;换句话说,是一个函数r -> r

你可以凭空召唤函数,因为如果你是柏拉图主义者,我想它们总是存在的.

当您使用do表示法时,您是“inside”,因此上下文r是隐式的。换句话说,您调用一个返回r值的函数,当您使用<-箭头时,您只需获得该上下文。

票数 13
EN

Stack Overflow用户

发布于 2018-02-24 10:42:54

您可以通过执行一些替换来使自己相信它是有效的。首先看一下createUser的签名。让我们“展开”Reader的定义

代码语言:javascript
复制
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表示法只是语法糖,表达式:

代码语言:javascript
复制
do permissions <- ask
   if hasPermission "admin" permissions
     then map Just newUser
     else pure Nothing

脱糖

代码语言:javascript
复制
ask >>= \permissions -> 
  if hasPermission "admin" permissions
  then map Just newUser
  else pure Nothing

要理解这一点,您必须查找ask>>=pure for ReaderT的定义。让我们执行另一轮替换:

代码语言:javascript
复制
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打开该函数并使用提供的参数调用它:

代码语言:javascript
复制
runReader :: forall r a. Reader r a -> r -> a
runReader (ReaderT f) r = f r
票数 3
EN

Stack Overflow用户

发布于 2022-05-02 08:36:09

部分函数类型(->) r是函子,即r->a是任何类型a的容器(大小为2的List a等效于函数Bool->a)。此外,它也是一个单曲。

代码语言:javascript
复制
instance Monad ((->) r) where
    f >>= k = \ r -> k (f r) r

它满足MonadReader类型类,被称为简单读取器monad,并可能被赋予类型同义词Reader r

ask返回应用于相同类型的r的monad (->) r) r,然后我们可以绑定它。

更好地理解部分函数类型(->) r

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46738184

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档