我想你很困惑
(x, g') <- return $ random g
这确实创建了一个新的单子动作State StdGen (a, StdGen)
,执行该动作以提取其结果(a, StdGen)
。
产生混淆是有充分理由的,因为代码实际上等同于
let (x, g') = random g
没有构建单子动作,从而导致更直接的代码。这种转换在任何单子中都是正确的,而不仅仅是State
一个。
无论如何,技术部分:(x, g') <- return $ random g
片段意味着
(x, g') <- State (\g'' -> (random g, g''))
我们可以看到一元动作采用当前状态g''
(与 g 具有相同的值),然后不修改它((..., g'')
部分),同时返回生成的值random g
((random g, ...)
部分)。
这有点傻,因为我们甚至不需要阅读 g''
,因为我们正在使用random g
!
所以,我们正在使用
do g <- State (\g'' -> (g'', g''))
(x, g') <- State (\g'' -> (random g, g''))
...
当我们可以改为使用
do (x, g') <- State (\g'' -> (random g'', g''))
...
在库中调用
do (x, g') <- gets random
...
好的,混乱似乎在do put g' ; return x
. 这被分解为绑定符号,如下所示
{ definitions }
put g' = State $ \s -> ((), g')
return x = State $ \s -> (x , s )
do put g ; return x
= { definitions, desugaring }
(State $ \s -> ((), g'))
>>=
(\_ -> State $ \s -> (x , s ))
= { definition of >>= }
State $ \s -> let (v,s') = (\s -> ((), g')) s
in runState ((\_ -> State $ \s -> (x , s )) v) s'
= { beta-reduction (application) }
State $ \s -> let (v,s') = ((), g')
in runState (State $ \s -> (x , s )) s'
= { beta-reduction (let) }
State $ \s -> runState (State $ \s -> (x , s )) g'
= { runState (State y) = y }
State $ \s -> (\s -> (x , s )) g'
= { beta-reduction }
State $ \s -> (x , g')
因此, 的效果do put g' ; return x
是将状态修改为g'
(覆盖前一个s
)并x
作为计算的最终值(与 一起g'
)产生。