You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: _chapters/randmonad.md
+61-7Lines changed: 61 additions & 7 deletions
Original file line number
Diff line number
Diff line change
@@ -35,11 +35,11 @@ For example:
35
35
-- (4,1868869221)
36
36
-- >>> rollDie1 1868869221
37
37
-- (1,166005888)
38
-
rollDie1::Seed-> (Int, Seed)
38
+
rollDie1::Seed-> (Seed, Int)
39
39
rollDie1 s =
40
40
let s' = nextRand s
41
41
n = genRand 16 s'
42
-
in (n, s')
42
+
in (s', n)
43
43
```
44
44
45
45
And if we want a sequence of dice rolls:
@@ -48,12 +48,12 @@ And if we want a sequence of dice rolls:
48
48
--| Roll a six-sided die `n` times.
49
49
-- >>> diceRolls1 3 123
50
50
-- ([5,4,1],166005888)
51
-
diceRolls1::Int->Seed-> ([Int], Seed)
51
+
diceRolls1::Int->Seed-> (Seed, [Int])
52
52
diceRolls1 0 s = ([], s)
53
53
diceRolls1 n s =
54
-
let (r, s') = rollDie1 s
55
-
(rolls, s'') = diceRolls1 (n-1) s'
56
-
in (r:rolls, s'')
54
+
let (s', r) = rollDie1 s
55
+
(s'', rolls) = diceRolls1 (n-1) s'
56
+
in (s'', r:rolls)
57
57
```
58
58
59
59
But keeping track of the various seeds (`s`,`s'`,`s''`) is tedious and error prone. Let's invent a monad which manages the seed for us. The seed will be threaded through all of our functions implicitly in the monadic return type.
@@ -261,4 +261,58 @@ Finally, how we can use this?
261
261
(1218640798,5)
262
262
```
263
263
264
-
`next` is used on `rollDie` to get the function of type `Seed -> (Seed, a)`. We then call this function with a seed value of `123`, to get a new seed and a dice roll
264
+
`next` is used on `rollDie` to get the function of type `Seed -> (Seed, a)`. We then call this function with a seed value of `123`, to get a new seed and a dice roll.
265
+
266
+
Now, here's how we get a list of dice rolls using a direct adaptation of our previous code, but trusting the `Rand` monad to thread the `Seed` through for us. No more messy wiring up of parameters and inventing arbitrary variable names.
267
+
268
+
```haskell
269
+
-- | Roll a six-sided die `n` times.
270
+
-- >>> runState (diceRolls 3) 123
271
+
-- ([5,4,1],166005888)
272
+
diceRolls :: Int -> Rand [Int]
273
+
diceRolls 0 = pure []
274
+
diceRolls n = do
275
+
r <- rollDie
276
+
rest <- diceRolls (n-1)
277
+
pure (r:rest)
278
+
```
279
+
280
+
## State Monad
281
+
282
+
Of course, Haskell libraries are extensive, and if you can think of a monad that's useful, there's probably a version of it already in the libraries somewhere.
283
+
284
+
Actually, we'll use two libraries. We'll replace our `Seed` type with `StdGen` and `nextRand` with `randomR`, both from `System.Random`. We'll replace our custom `Rand` monad with `Control.Monad.State`.
285
+
286
+
In `diceRolls`, we'll also replace the recursive list construction, with `replicateM`, which just runs a function with a monadic effect `n` times, placing the results in a list.
287
+
288
+
```haskell
289
+
moduleStateDie
290
+
where
291
+
292
+
importSystem.Random
293
+
importControl.Monad.State
294
+
295
+
-- in System.Random seeds have type StdGen, we'll make the starting seed:
296
+
seed::StdGen
297
+
seed = mkStdGen 123
298
+
299
+
-- remake the Rand monad, but using the State monad to store the seed
300
+
typeRanda=StateStdGena
301
+
302
+
-- remake genRand using the randomR function from System.Random
303
+
-- instead of our custom nextRand function
304
+
genRand::Int->Int->RandInt
305
+
genRand lower upper = state (randomR (lower,upper))
306
+
307
+
--| A function that simulates rolling a six-sided dice
0 commit comments