我通过示例阅读 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标记。
答案 0 :(得分:10)
我目前没有PureScript环境,因此我将尝试从Haskell的角度回答,并希望它有所帮助。
Reader 实际上只是一个&#39;包装器&#39;围绕一个函数,所以当你得到一个Reader r r
时,你真的只能得到一个从r
到r
的读者;换句话说,一个函数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
查找>>=
,pure
和ReaderT
的定义。让我们进行另一轮替换:
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