Jay*_*Jay 4 monads haskell state-monad
我查看了State monad的一些教程,我想我有了这个想法.
例如,在这个很好的教程中:
import Data.Word
type LCGState = Word32
lcg :: LCGState -> (Integer, LCGState)
lcg s0 = (output, s1)
where s1 = 1103515245 * s0 + 12345
output = fromIntegral s1 * 2^16 `div` 2^32
getRandom :: State LCGState Integer
getRandom = get >>= \s0 -> let (x,s1) = lcg s0
in put s1 >> return x
Run Code Online (Sandbox Code Playgroud)
好的,所以我可以使用getRandom:
*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 1
(16838,1103527590)
Run Code Online (Sandbox Code Playgroud)
但是我每次打电话都需要把种子传给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
Run Code Online (Sandbox Code Playgroud)
所以我可能误解了State monad,因为我在大多数教程中看到的似乎并不是"持久"状态,而只是一种方便的线程状态.
那么......我怎么能拥有自动初始化的状态(可能来自某个使用时间的函数和其他不可预测的数据),就像Random模块那样?
非常感谢!
randomRIO使用IOmonad.这似乎在解释器中很好用,因为解释器也可以在IOmonad中工作.这就是你在你的例子中所看到的; 你实际上不能在代码的顶级做到这一点 - 无论如何你必须把它放在像所有monad一样的do-expression中.
在一般代码中,你应该避免IO monad,因为一旦你的代码使用IO monad,它就永远与外部状态联系在一起 - 你无法摆脱它(即如果你有使用IO monad的代码,任何代码调用它也必须使用IO monad;没有安全的方法来"离开"它.所以IOmonad应该只用于访问外部环境,绝对需要的东西.
对于像本地自包含状态这样的东西,你不应该使用IO monad.你可以State像你提到的那样使用monad,或者你可以使用STmonad.ST monad包含许多与IO monad相同的功能; 即有STRef可变细胞,类似于IORef.与IO相比,ST的好处在于,当你完成后,你可以调用runSTST monad来获取monad中的计算结果,这是IO无法做到的.
至于"隐藏"状态,这只是Haskell for monads中do-expressions语法的一部分.如果您认为需要显式传递状态,那么您没有正确使用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
Run Code Online (Sandbox Code Playgroud)
这是使用ST monad的代码:
import Control.Monad.ST
import Data.STRef
bar :: Int
bar = runST (do x <- newSTRef 1
modifySTRef x (+ 2)
readSTRef x)
-- bar == 3
Run Code Online (Sandbox Code Playgroud)
代码的简单性基本相同; 除了在后一种情况下我们可以从monad中获取值,而在前者中我们不能将其放入另一个IO计算中.