我想你很困惑
(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')产生。