1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
{- Author: Jeff Newbern
Maintainer: Jeff Newbern <jnewbern@nomaware.com>
Time-stamp: <Thu Jul 24 13:39:30 2003>
License: GPL
-}
{- DESCRIPTION
Example 15 - Using the State monad
Usage: Compile the code and execute the command.
It will print two identical random values of type MyType.
The first value is computed without the State monad and
the second is computed using the State monad.
The MyType values are random but obey some internal
invariants:
o the Int value is in the range 1-100.
o the Char value is in the range 'a'-'z'
o the absolute value of the second Int value is
less than or equal to the value of the first Int value
Try: ./ex15
-}
import Monad
import System
import IO
import Random
import Control.Monad.State
-- This is the type that we want to generate random values of
data MyType = MT Int Bool Char Int deriving Show
{- Without using the State monad, we would have to thread the
random number generator state by hand. The function would
look like this:
-}
makeRandomValue :: StdGen -> (MyType, StdGen)
makeRandomValue g = let (n,g1) = randomR (1,100) g
(b,g2) = random g1
(c,g3) = randomR ('a','z') g2
(m,g4) = randomR (-n,n) g3
in (MT n b c m, g4)
{- Using the State monad, we can define a function that returns
a random value and updates the random generator state at
the same time.
-}
getAny :: (Random a) => State StdGen a
getAny = do g <- get
(x,g') <- return $ random g
put g'
return x
-- similar to getAny, but it bounds the random value returned
getOne :: (Random a) => (a,a) -> State StdGen a
getOne bounds = do g <- get
(x,g') <- return $ randomR bounds g
put g'
return x
{- Using the State monad with StdGen as the state, we can build
random complex types without manually threading the
random generator states through the code.
-}
makeRandomValueST :: StdGen -> (MyType, StdGen)
makeRandomValueST = runState (do n <- getOne (1,100)
b <- getAny
c <- getOne ('a','z')
m <- getOne (-n,n)
return (MT n b c m))
-- print a random value of MyType, showing the two implementations
-- are equivalent
main :: IO ()
main = do g <- getStdGen
print $ fst $ makeRandomValue g
print $ fst $ makeRandomValueST g
-- END OF FILE
|