Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Haskell中以隐藏的方式初始化状态(就像PRNG一样)?_Haskell_Monads_State Monad - Fatal编程技术网

如何在Haskell中以隐藏的方式初始化状态(就像PRNG一样)?

如何在Haskell中以隐藏的方式初始化状态(就像PRNG一样)?,haskell,monads,state-monad,Haskell,Monads,State Monad,我看了一些关于国家单子的教程,我想我明白了 例如,例如: 好的,我可以使用getRandom: *Main> runState getRandom 0 (0,12345) *Main> runState getRandom 0 (0,12345) *Main> runState getRandom 1 (16838,1103527590) 但每次我打电话给PRNG时,我仍然需要把种子传给它。我知道 Haskell实施中可用的PRNG不需要: Pre

我看了一些关于国家单子的教程,我想我明白了

例如,例如:

好的,我可以使用getRandom:

*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 1              
(16838,1103527590)
但每次我打电话给PRNG时,我仍然需要把种子传给它。我知道 Haskell实施中可用的PRNG不需要:

Prelude> :module Random
Prelude Random> randomRIO (1,6 :: Int)
(...) -- GHC prints some stuff here
6
Prelude Random> randomRIO (1,6 :: Int)
1
所以我可能误解了国家单子,因为我在大多数教程中都能看到 似乎不是“持久”状态,只是一种方便的线程状态方式

所以。。。如何使状态自动初始化(可能是从某些 使用时间和其他不太可预测的数据(如随机模块)的函数 是吗

非常感谢

secretStateValue :: IORef SomeType
secretStateValue = unsafePerformIO $ newIORef initialState
{-# NOINLINE secretStateValue #-}
现在,在IO monad中使用normal访问secretStateValue

所以我可能误解了State monad,因为我在大多数教程中看到的似乎不是“持久”状态,而是线程状态的一种便捷方式

状态monad正是关于在某个范围内线程化状态的


如果您想要顶级状态,那就在语言之外(并且您必须使用全局可变变量)。请注意,这可能会使代码的线程安全性变得复杂——该状态是如何初始化的?什么时候?通过哪个线程?

randomRIO
使用
IO
monad。这似乎在解释器中工作得很好,因为解释器也在
IO
monad中工作。这就是你在例子中看到的;实际上,您不能在代码的顶层这样做——您必须像所有单子一样将其放入do表达式中

在一般代码中,您应该避免IO monad,因为一旦您的代码使用IO monad,它将永远与外部状态绑定在一起——您无法摆脱它(即,如果您有使用IO monad的代码,任何调用它的代码也必须使用IO monad;没有安全的方法“摆脱”它)。因此,
IO
monad应该只用于访问外部环境等绝对需要的事情

对于像本地自包含状态这样的事情,您不应该使用IO monad。您可以使用前面提到的
State
monad,也可以使用
ST
monad。ST单子包含许多与IO单子相同的特性;i、 e.有
STRef
可变细胞,类似于
IORef
。与IO相比,ST的优点在于,当您完成时,您可以调用ST monad上的
runST
,从monad中获得计算结果,这是IO无法做到的

至于“隐藏”状态,这只是Haskell for Monad中do表达式语法的一部分。如果您认为需要显式传递状态,那么您没有正确使用monad语法

以下是在IO Monad中使用IORef的代码:

import Data.IORef
foo :: IO Int -- this is stuck in the IO monad forever
foo = do x <- newIORef 1
         modifyIORef x (+ 2)
         readIORef x
-- foo is an IO computation that returns 3
import Data.IORef
foo::IO Int——这永远卡在IO monad中

foo=do x说到它,
Random
使用
atomicModifyIORef
而不是
readIORef
+
writeIORef
对;这样做似乎是一个好主意。事实上,如果使用atomicModifyIORef从多个线程进行访问,这是一个更好的主意。谢谢!这很有帮助。关于你的评论,“在前者中,我们不能不将其放入另一个IO计算中”——我支持这就是为什么当你组合多个单体(使用单体转换器)时,IO单体需要位于最里面的原因,那么?现在我想起来了,PRNG必然会使用IO单体,对吗?它需要熵,所以它需要来自“某处”的数据。实际上,它至少需要一个引用不透明的函数(否则它就不会有用,例如,对于加密代码)!:-)
import Data.IORef
foo :: IO Int -- this is stuck in the IO monad forever
foo = do x <- newIORef 1
         modifyIORef x (+ 2)
         readIORef x
-- foo is an IO computation that returns 3
import Control.Monad.ST
import Data.STRef
bar :: Int
bar = runST (do x <- newSTRef 1
                modifySTRef x (+ 2)
                readSTRef x)
-- bar == 3