``````type IO a  =  RealWorld -> (a, RealWorld)
``````

（是的，我知道这不是IO的GHC实现，而只是理解它的工具。）原因是，在我的应用程序（游戏）中，我现在有两个模式，用两个不同的`RealWorld`替换这里。 在一个中，它是游戏的状态，而在另一个中，它只是一个`StdGen`随机数种子。 我当然现在有两对这样的类型：

``````-- | Easily return a specified value as well as the new random number generator
type ReturnRNG a = (a, StdGen)

-- | Take an RNG and return it and another value.
-- (This is basically like the IO type but with the StdGen instead of RealWorld.)
type WithRNG a = StdGen -> ReturnRNG a

-- | Easily return a specified value as well as the new GameState
type ReturnGS a = (a, GameState)

-- | Use a GameState and return a value with the updated GameState.
-- (This is like IO.)
type WithGS a = GameState -> ReturnGS a
``````

（是的，我可以用两个参数将它们抽象成一对，但我还没有完成它。）当然，你可以看到我的`WithGS a``WithRNG a`类型（类型同义词）完全类似于上面的`IO a`

``````-- | Returns a random position for the given size.
randomPos :: (Int, Int)          -- ^ The size
-> WithRNG (Int, Int)  -- ^ The result (0 up to 1 less than the size) and new RNG seed
randomPos (w, h) r0 = ((x, y), r2)
where
(x, r1) = randomR (0, w - 1) r0
(y, r2) = randomR (0, h - 1) r1
``````

``````-- (Not working example)
randomPosSt (w, h) = do
x <- randomR (0, w - 1)
y <- randomR (0, h - 1)
return (x, y)
``````

...但是具有完全相同的方法签名和语义。 这看起来应该可以遵循前面给出这个例子的教程：

``````(>>=) :: IO a -> (a -> IO b) -> IO b
(action1 >>= action2) world0 =
let (a, world1) = action1 world0
(b, world2) = action2 a world1
in (b, world2)
``````

``````-- THIS DOES NOT WORK
-- | Make a State Monad with random number generator - like WithRNG above
type RandomState = State StdGen

-- | Returns a random position for the given size.
randomPosSt :: (Int, Int)                  -- ^ The size
-> RandomState (Int, Int)  -- ^ The result (0 up to 1 less than the size) and new RNG seed
``````

``````-- | Maps the specified method, which must take a RNG as the last parameter,
-- over all the elements of the list, propagating the RNG and returning it.
-- TODO: Implement this without using recursion? Using a fold?
mapRandom :: (a -> WithRNG b) -- ^ The function to map (that takes a RNG)
-> [a] -- ^ The input list
-> WithRNG [b] -- ^ The RNG to return
mapRandom func [] r0 = ([], r0)
mapRandom func (x:xs) r0 = (mapped : rest, r2)
where
(mapped, r1) = func x r0
(rest, r2)   = mapRandom func xs r1
``````

1 个回复1

===============>>#1 票数：9 已采纳

``````import System.Random

newtype State s a = State { runState :: s -> (a, s) }

return a = State (\s -> (a, s))

m >>= f = State (\s0 ->
let (a, s1) = runState m s0
in  runState (f a) s1 )

randomPos :: (Int, Int) -> State StdGen (Int, Int)
randomPos (w, h) = do
x <- State \$ randomR (0, w - 1)
y <- State \$ randomR (0, h - 1)
return (x, y)
``````

``````State :: (s -> (a, s)) -> State s a
``````

..和`randomR (lo, hi)`具有直接包含在`State`的正确类型：

``````randomR (1, 6)          :: StdGen -> (Int, StdGen)
StateT \$ randomR (1, 6) :: State StdGen Int
``````

`State`构造函数接受状态传递函数并创建一个合适的值以在`State` monad中使用。 然后，当您使用monad完成后，您可以使用`runState`将monad转换回等效的状态传递函数：

``````runState :: State s a -> (s -> (a, s))

runState (randomPos (5, 6)) :: StdGen -> ((Int, Int), StdGen)
``````

``````mapRandom
:: ( a  -> (StdGen -> ( b , StdGen)))
-> ([a] -> (StdGen -> ([b], StdGen)))
mapRandom f xs = runState \$ mapM (State . f) xs
``````

``````mapM :: (a -> State s b) -> [a] -> State s [b]
``````