理解 Reader monad
Understanding the Reader monad
我正在阅读 PureScript by Example 并阅读了介绍 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
它似乎凭空创造了一个 Reader
当我阅读有关 State
monad 的内容时,它与 get
函数具有相同的概念。以及文字说明:
状态被实现为隐藏在 State monad 的数据构造函数中的函数参数,因此没有显式引用可传递。
我猜这是关键,Reader 也发生了同样的事情,但我不明白它是如何工作的...
当上面的例子是运行通过runReader
时,怎么突然出现了提供的值作为ask
的结果? ask
的 Haskell 文档说:检索 monad 环境。 但我的困惑是 从哪里 ?在我看来,一个值被传递给 runReader
,被存储在 某个地方 ,然后要获取它 - 你调用 ask
...感觉。
虽然示例是 PureScript,但我猜任何 Haskell 识字的人也能回答,因此 Haskell 标签。
我目前没有 PureScript 环境,所以我会尝试从 Haskell 的角度回答,希望对您有所帮助。
一个Reader实际上只是一个函数周围的'wrapper',所以当你得到一个Reader r r
时,你实际上只得到一个reader 从 r
到 r
;换句话说,函数 r -> r
.
你可以凭空召唤函数,因为,如果你是柏拉图主义者,我想它们总是存在的......
当您使用 do
表示法时,您是 'inside the monad',因此上下文 r
是隐含的。换句话说,您调用一个 returns 值为 r
的函数,当您使用 <-
箭头时,您只需获取该上下文。
您可以通过执行一些替换来说服自己它有效。先看createUser
的签名。让我们"unroll"定义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
脱糖为
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
包装的函数,它通过您的表达式将一个值("environment")串联起来。 runReader
展开函数并使用提供的参数调用它:
runReader :: forall r a. Reader r a -> r -> a
runReader (ReaderT f) r = f r
部分函数类型 (->) r
是一个函子,即 r->a
是任何类型 a
的容器(大小为 2 的 List a
等同于函数 Bool->a
) 。而且,它也是一个monad
instance Monad ((->) r) where
f >>= k = \ r -> k (f r) r
它满足 MonadReader
类型 class 并且被称为简单的 reader monad 并且可能被赋予类型同义词 Reader r
.
ask
returns 这个 monad (->) r) r
应用于同一类型 r
,然后我们可以绑定它。
更好地理解偏函数类型(->) r
。
我正在阅读 PureScript by Example 并阅读了介绍 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
它似乎凭空创造了一个 Reader
当我阅读有关 State
monad 的内容时,它与 get
函数具有相同的概念。以及文字说明:
状态被实现为隐藏在 State monad 的数据构造函数中的函数参数,因此没有显式引用可传递。
我猜这是关键,Reader 也发生了同样的事情,但我不明白它是如何工作的...
当上面的例子是运行通过runReader
时,怎么突然出现了提供的值作为ask
的结果? ask
的 Haskell 文档说:检索 monad 环境。 但我的困惑是 从哪里 ?在我看来,一个值被传递给 runReader
,被存储在 某个地方 ,然后要获取它 - 你调用 ask
...感觉。
虽然示例是 PureScript,但我猜任何 Haskell 识字的人也能回答,因此 Haskell 标签。
我目前没有 PureScript 环境,所以我会尝试从 Haskell 的角度回答,希望对您有所帮助。
一个Reader实际上只是一个函数周围的'wrapper',所以当你得到一个Reader r r
时,你实际上只得到一个reader 从 r
到 r
;换句话说,函数 r -> r
.
你可以凭空召唤函数,因为,如果你是柏拉图主义者,我想它们总是存在的......
当您使用 do
表示法时,您是 'inside the monad',因此上下文 r
是隐含的。换句话说,您调用一个 returns 值为 r
的函数,当您使用 <-
箭头时,您只需获取该上下文。
您可以通过执行一些替换来说服自己它有效。先看createUser
的签名。让我们"unroll"定义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
脱糖为
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
包装的函数,它通过您的表达式将一个值("environment")串联起来。 runReader
展开函数并使用提供的参数调用它:
runReader :: forall r a. Reader r a -> r -> a
runReader (ReaderT f) r = f r
部分函数类型 (->) r
是一个函子,即 r->a
是任何类型 a
的容器(大小为 2 的 List a
等同于函数 Bool->a
) 。而且,它也是一个monad
instance Monad ((->) r) where
f >>= k = \ r -> k (f r) r
它满足 MonadReader
类型 class 并且被称为简单的 reader monad 并且可能被赋予类型同义词 Reader r
.
ask
returns 这个 monad (->) r) r
应用于同一类型 r
,然后我们可以绑定它。
更好地理解偏函数类型(->) r
。