如何在Haskell的System.Random.Stateful中使用do notation组合随机值
你写的example3里的嵌套do完全是不必要的,uniformRM在runStateGen提供的状态上下文中会直接返回生成好的随机值,不需要二次绑定。正确的重写代码如下:
example1 :: Bool example1 = fst $ runStateGen g do x <- uniformRM (False, True) y <- uniformRM (False, True) return $ x == y
你之前的实现之所以有两层state运行的冗余,是因为你自定义的uniformR函数内部已经调用runStateGen跑完了单次随机计算,返回纯值和新生成器,外层又套了一层State monad做状态管理,相当于重复做了两次状态封装和运行。
而直接用System.Random.Stateful原生接口时,runStateGen会把你传入的纯StdGen包装成有状态的生成器实例,同一个do块内的所有uniformRM调用都共享这个状态上下文,生成器的状态更新全部在同一个上下文里自动完成,没有额外开销。
uniformRM的类型为(UniformRange a, StatefulGen g m) => (a, a) -> m a,在runStateGen的参数do块中使用时,m就是内置的状态monad,因此x <- uniformRM ...绑定得到的x就是Bool类型的纯值,不需要再做二次monadic绑定,这也是你之前example3写法繁琐的核心原因。
不需要额外引入monad transformer。System.Random.Stateful已经把随机数生成的状态操作封装成了符合Monad约束的接口,直接用它提供的do notation写法就可以满足需求,和你之前用State monad的写法几乎一致,还避免了自定义uniformR带来的冗余。
内容的提问来源于stack exchange,提问作者bwroga

