put 命令在使用 State monad 的典型函数中做了什么?

What does the put command do in a typical function using the State monad?

这是来自 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

我觉得你被

搞糊涂了
(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, ...) 部分)。

这有点傻,因为我们甚至不需要 read 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')。

假设我们的状态存储在一个文件中。这是 getAny 用 Javascript/Python-like 语言表达的内容:

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) 必须 return 两个值,所以我有它 return 一个数组。

现在考虑在这个调用序列中发生了什么:

 a = getAny()       same as:    do a <- getAny
 b = getAny()                      b <- getAny
 c = getAny()                      c <- getAny

对于 a = getAny():

  • 从文件中读取状态
  • 它被赋予 random 其中 return 两个值
  • 第二个值写入文件
  • 第一个值被 returned 并存储在变量 a

然后 b = getAny():

  • 刚刚写入文件的状态被读回
  • 它被馈送到 random() 产生一个值和新状态
  • 新状态写入文件
  • 新值被 return 编辑并存储在变量 b

等...

现在回答你的问题:

what does the put g' actually do?

它用新值更新状态。

Why wouldn't the g' actually be lost?

newg 只是一个局部变量,所以它的值会丢失,除非我们将它保存在某个地方。