理解简单 Reader monad 的 do 符号:a <- (*2), b <- (+10), return (a+b)
Understanding do notation for simple Reader monad: a <- (*2), b <- (+10), return (a+b)
instance Monad ((->) r) where
return x = \_ -> x
h >>= f = \w -> f (h w) w
import Control.Monad.Instances
addStuff :: Int -> Int
addStuff = do
a <- (*2)
b <- (+10)
return (a+b)
我试图通过展开 do 符号来理解这个 monad,因为我认为 do 符号隐藏了发生的事情。
如果我没理解错的话,是这样的:
(*2) >>= (\a -> (+10) >>= (\b -> return (a+b)))
现在,如果我们采用 >>=
的规则,我们必须将 (*2)
理解为 h
,将 (\a -> (+10) >>= (\b -> return (a+b)))
理解为 f
。将 h
应用于 w
很容易,假设它是 2w
(我不知道 2w
在 haskell 中是否有效,但只是为了推理保持这种方式。现在我们必须将 f
应用到 h w
或 2w
。好吧,f
只是 returns (+10) >>= (\b -> return (a+b))
对于特定 a
,在我们的例子中是 2w
,所以 f (hw)
是 (+10) >>= (\b -> return (2w+b))
。我们必须首先了解 (+10) >>= (\b -> return (2w + b))
发生了什么,然后才能最终将其应用到 w
.
现在我们用我们的规则重新识别 (+10) >>= (\b -> return (2w + b))
,所以 h
是 +10
,f
是 (\b -> return (2w + b))
。让我们先做h w
。我们得到 w + 10
。现在我们需要将 f
应用到 h w
。我们得到 (return (2w + w + 10))
.
所以 (return (2w + w + 10))
是我们需要在第一个 >>=
中应用到 w
的东西,我们已经厌倦了 uwind。但是我完全迷路了,我不知道发生了什么。
我的想法是否正确?这太令人困惑了。有更好的思路吗?
您忘记了运算符 >>=
不只是 return f (h w) w
,而是 \w -> f (h w) w
。也就是说,它 return 是一个函数,而不是一个数字。
由于错误地替换它,您丢失了最外层的参数 w
,因此它在您的最终表达式中保持自由状态也就不足为奇了。
要正确地做到这一点,您必须完全用函数体替换它们的调用,而不能丢弃任何东西。
如果你替换最外面的>>=
,你会得到:
(*2) >>= (\a -> ...)
==
\w -> (\a -> ...) (w*2) w
然后,如果你替换最里面的 >>=
,你会得到:
\a -> (+10) >>= (\b -> return (a+b))
==
\a -> \w1 -> (\b -> return (a+b)) (w1 + 10) w1
请注意,我使用 w1
而不是 w
。这是为了避免以后组合替换时发生名称冲突,因为这两个 w
来自两个不同的 lambda 抽象,所以它们是不同的变量。
最后,替换 return
:
return (a+b)
==
\_ -> a+b
现在将最后一个替换插入到前一个替换中:
\a -> (+10) >>= (\b -> return (a+b))
==
\a -> \w1 -> (\b -> return (a+b)) (w1 + 10) w1
==
\a -> \w1 -> (\b -> \_ -> a+b) (w1 + 10) w1
最后将其插入到第一个替换中:
(*2) >>= (\a -> ...)
==
\w -> (\a -> ...) (w*2) w
==
\w -> (\a -> \w1 -> (\b -> \_ -> a+b) (w1 + 10) w1) (w*2) w
现在所有的替代品都是竞争性的,我们可以减少。从应用最里面的 lambda \b -> ...
:
开始
\w -> (\a -> \w1 -> (\_ -> a+w1+10) w1) (w*2) w
现在应用新的最里面的 lambda \_ -> ...
:
\w -> (\a -> \w1 -> a+w1+10) (w*2) w
现在申请\a -> ...
:
\w -> (\w1 -> w*2+w1+10) w
最后应用唯一剩余的 lambda \w1 -> ...
:
\w -> w*2+w+10
瞧!整个函数减少到 \w -> (w*2) + (w+10)
,完全符合预期。
直观上,<-
右侧的每个函数调用都被赋予一个额外的参数,您可以将其视为 addStuff
本身的参数。
拿
addStuff :: Int -> Int
addStuff = do
a <- (*2)
b <- (+10)
return (a+b)
然后变成
addStuff :: Int -> Int
addStuff x = let a = (*2) x
b = (+10) x
in (a+b)
如果您对 (->) r
使用 MonadReader
实例,它看起来会少一些 "strange",它提供了 ask
作为直接访问隐式值的方法.
import Control.Monad.Reader
addStuff :: Int -> Int
addStuff = do
x <- ask -- ask is literally just id in this case
let a = x * 2
let b = x + 10
return (a + b)
首先,我们明确地写出您定义中的隐式参数,
addStuff :: Int -> Int
addStuff = do
a <- (*2)
b <- (+10)
return (a+b)
=
addStuff :: Int -> Int
addStuff x = ( do
a <- (*2)
b <- (+10)
return (a+b) ) x
=
....
然后,
return x = const x
(f =<< h) w = f (h w) w -- (f =<< h) = (h >>= f)
应该更容易理解和替换定义,逐行:
....
=
( (*2) >>= (\a -> -- (h >>= f) =
(+10) >>= (\b ->
const (a+b) ) ) ) x
=
( (\a -> -- = (f =<< h)
(+10) >>= (\b ->
const (a+b) ) ) =<< (*2) ) x -- (f =<< h) w =
=
(\a ->
(+10) >>= (\b ->
const (a+b) ) ) ( (*2) x) x -- = f (h w) w
=
( let a = (*2) x in -- parameter binding
(+10) >>= (\b ->
const (a+b) ) ) x
=
let a = (*2) x in -- float the let
((\b ->
const (a+b) ) =<< (+10) ) x -- swap the >>=
=
let a = (*2) x in
(\b -> -- (f =<< h) w =
const (a+b) ) ( (+10) x) x -- = f (h w) w
=
let a = (*2) x in
(let b = (+10) x in -- application
const (a+b) ) x
=
let a = (*2) x in -- do a <- (*2)
let b = (+10) x in -- b <- (+10)
const (a+b) x -- return (a+b)
reader monad 的本质是应用在所有调用之间共享的相同参数。
instance Monad ((->) r) where
return x = \_ -> x
h >>= f = \w -> f (h w) w
import Control.Monad.Instances
addStuff :: Int -> Int
addStuff = do
a <- (*2)
b <- (+10)
return (a+b)
我试图通过展开 do 符号来理解这个 monad,因为我认为 do 符号隐藏了发生的事情。
如果我没理解错的话,是这样的:
(*2) >>= (\a -> (+10) >>= (\b -> return (a+b)))
现在,如果我们采用 >>=
的规则,我们必须将 (*2)
理解为 h
,将 (\a -> (+10) >>= (\b -> return (a+b)))
理解为 f
。将 h
应用于 w
很容易,假设它是 2w
(我不知道 2w
在 haskell 中是否有效,但只是为了推理保持这种方式。现在我们必须将 f
应用到 h w
或 2w
。好吧,f
只是 returns (+10) >>= (\b -> return (a+b))
对于特定 a
,在我们的例子中是 2w
,所以 f (hw)
是 (+10) >>= (\b -> return (2w+b))
。我们必须首先了解 (+10) >>= (\b -> return (2w + b))
发生了什么,然后才能最终将其应用到 w
.
现在我们用我们的规则重新识别 (+10) >>= (\b -> return (2w + b))
,所以 h
是 +10
,f
是 (\b -> return (2w + b))
。让我们先做h w
。我们得到 w + 10
。现在我们需要将 f
应用到 h w
。我们得到 (return (2w + w + 10))
.
所以 (return (2w + w + 10))
是我们需要在第一个 >>=
中应用到 w
的东西,我们已经厌倦了 uwind。但是我完全迷路了,我不知道发生了什么。
我的想法是否正确?这太令人困惑了。有更好的思路吗?
您忘记了运算符 >>=
不只是 return f (h w) w
,而是 \w -> f (h w) w
。也就是说,它 return 是一个函数,而不是一个数字。
由于错误地替换它,您丢失了最外层的参数 w
,因此它在您的最终表达式中保持自由状态也就不足为奇了。
要正确地做到这一点,您必须完全用函数体替换它们的调用,而不能丢弃任何东西。
如果你替换最外面的>>=
,你会得到:
(*2) >>= (\a -> ...)
==
\w -> (\a -> ...) (w*2) w
然后,如果你替换最里面的 >>=
,你会得到:
\a -> (+10) >>= (\b -> return (a+b))
==
\a -> \w1 -> (\b -> return (a+b)) (w1 + 10) w1
请注意,我使用 w1
而不是 w
。这是为了避免以后组合替换时发生名称冲突,因为这两个 w
来自两个不同的 lambda 抽象,所以它们是不同的变量。
最后,替换 return
:
return (a+b)
==
\_ -> a+b
现在将最后一个替换插入到前一个替换中:
\a -> (+10) >>= (\b -> return (a+b))
==
\a -> \w1 -> (\b -> return (a+b)) (w1 + 10) w1
==
\a -> \w1 -> (\b -> \_ -> a+b) (w1 + 10) w1
最后将其插入到第一个替换中:
(*2) >>= (\a -> ...)
==
\w -> (\a -> ...) (w*2) w
==
\w -> (\a -> \w1 -> (\b -> \_ -> a+b) (w1 + 10) w1) (w*2) w
现在所有的替代品都是竞争性的,我们可以减少。从应用最里面的 lambda \b -> ...
:
\w -> (\a -> \w1 -> (\_ -> a+w1+10) w1) (w*2) w
现在应用新的最里面的 lambda \_ -> ...
:
\w -> (\a -> \w1 -> a+w1+10) (w*2) w
现在申请\a -> ...
:
\w -> (\w1 -> w*2+w1+10) w
最后应用唯一剩余的 lambda \w1 -> ...
:
\w -> w*2+w+10
瞧!整个函数减少到 \w -> (w*2) + (w+10)
,完全符合预期。
直观上,<-
右侧的每个函数调用都被赋予一个额外的参数,您可以将其视为 addStuff
本身的参数。
拿
addStuff :: Int -> Int
addStuff = do
a <- (*2)
b <- (+10)
return (a+b)
然后变成
addStuff :: Int -> Int
addStuff x = let a = (*2) x
b = (+10) x
in (a+b)
如果您对 (->) r
使用 MonadReader
实例,它看起来会少一些 "strange",它提供了 ask
作为直接访问隐式值的方法.
import Control.Monad.Reader
addStuff :: Int -> Int
addStuff = do
x <- ask -- ask is literally just id in this case
let a = x * 2
let b = x + 10
return (a + b)
首先,我们明确地写出您定义中的隐式参数,
addStuff :: Int -> Int
addStuff = do
a <- (*2)
b <- (+10)
return (a+b)
=
addStuff :: Int -> Int
addStuff x = ( do
a <- (*2)
b <- (+10)
return (a+b) ) x
=
....
然后,
return x = const x
(f =<< h) w = f (h w) w -- (f =<< h) = (h >>= f)
应该更容易理解和替换定义,逐行:
....
=
( (*2) >>= (\a -> -- (h >>= f) =
(+10) >>= (\b ->
const (a+b) ) ) ) x
=
( (\a -> -- = (f =<< h)
(+10) >>= (\b ->
const (a+b) ) ) =<< (*2) ) x -- (f =<< h) w =
=
(\a ->
(+10) >>= (\b ->
const (a+b) ) ) ( (*2) x) x -- = f (h w) w
=
( let a = (*2) x in -- parameter binding
(+10) >>= (\b ->
const (a+b) ) ) x
=
let a = (*2) x in -- float the let
((\b ->
const (a+b) ) =<< (+10) ) x -- swap the >>=
=
let a = (*2) x in
(\b -> -- (f =<< h) w =
const (a+b) ) ( (+10) x) x -- = f (h w) w
=
let a = (*2) x in
(let b = (+10) x in -- application
const (a+b) ) x
=
let a = (*2) x in -- do a <- (*2)
let b = (+10) x in -- b <- (+10)
const (a+b) x -- return (a+b)
reader monad 的本质是应用在所有调用之间共享的相同参数。