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

时间:2016-06-19 15:06:36

标签: haskell monads state-monad do-notation

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

2 个答案:

答案 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'一起)。