这是https://wiki.haskell.org/All_About_Monads的示例
它是使用State
monad通过一系列随机数生成命令来对StdGen
值进行线程化的示例。
如果我理解上一个return
正确执行的操作,则应该创建一个 new monad,并将x
作为值。但那么put g'
实际上做了什么?为什么g'
实际上不会丢失?
getAny :: (Random a) => State StdGen a
getAny = do g <- get
(x,g') <- return $ random g
put g'
return x
答案 0 :(得分:3)
假设我们的状态存储在一个文件中。以下是getAny
正在用Javascript / Python类语言表达的内容:
function getAny() {
var g = readStateFromFile("some-path") // the get call
var xg = random(g) // returns an array with two elements
var x = xg[0]
var newg = xg[1]
writeStateToFile("some-path", newg) // same as the put function call
return x
}
这里random(g)
必须返回两个值,所以我让它返回一个数组。
现在考虑在这一系列调用过程中会发生什么:
a = getAny() same as: do a <- getAny
b = getAny() b <- getAny
c = getAny() c <- getAny
代表a = getAny()
:
random
,返回两个值a
然后是b = getAny()
:
random()
,产生一个值和一个新状态b
等...
现在回答你的问题:
什么是g&#39;实际上呢?
它使用新值更新状态。
为什么g&#39;实际上是丢了?
newg
只是一个局部变量,所以除非我们将它保存在某处,否则它的值会丢失。
答案 1 :(得分:1)
我觉得你很困惑
(x, g') <- return $ random g
这确实会创建一个新的monadic动作State StdGen (a, StdGen)
,执行该动作以提取其结果(a, StdGen)
。
混淆是有充分理由的,因为代码实际上等同于
let (x, g') = random g
没有构建monadic动作,导致更直接的代码。
这种转换在任何monad中都是正确的,而不仅仅是State
。
无论如何,技术部分:(x, g') <- return $ random g
摘录表示
(x, g') <- State (\g'' -> (random g, g''))
我们可以看到monadic动作采用当前状态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'
一起)。