我正在阅读 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
? Haskell文档为 ask
说: 检索monad环境。 但我的困惑是 来自哪里?我看到它的方式,一个值传递给 runReader
,得到存储 某处,并得到它 - 你打电话 ask
......但这没有任何意义。
虽然这个例子是Purescript,但我猜测任何有Haskell识字的人也能够回答,因此Haskell标签。
我目前没有PureScript环境,因此我将尝试从Haskell的角度回答,并希望它有所帮助。
一个 读者 实际上只是一个函数的“包装器”,所以当你得到一个 Reader r r
,你真的只有一个读者 r
至 r
;换句话说,一个功能 r -> r
。
您 能够 因为如果你是柏拉图主义者,我认为他们总是存在......
当你使用 do
符号,你在'monad'中,所以上下文 r
是隐含的。换句话说,你调用一个返回的函数 r
价值,当你使用 <-
箭头,您只需获得该上下文。
您可以通过执行一些替换来说服自己它的工作原理。首先看看签名 createUser
。让我们“展开”的定义 Reader
:
createUser :: Reader Permissions (Maybe User)
{- definition of Reader -}
createUser :: ReaderT Permissions Identity (Maybe User)
该 ReaderT
type只有一个数据构造函数: 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