Reputation: 17051
I want to generate a list of random numbers, where the range of each random number is determined by elements of a provided list. I thought I had something that made sense, but I get errors I don't understand :(
Here's what I have:
useRNG nums min = do
generator <- get
let (val, generator') = randomR (min, 51) generator
put generator'
return $ val : nums
foldM useRNG [] [0 .. 50]
Can anyone help me out?
Upvotes: 1
Views: 231
Reputation: 40787
The problem is that useRNG
can generate all kinds of numbers (any instance of Random
), and work in all kinds of monads (any state monad whose state is an instance of RandomGen
), as can be seen from its inferred type signature:
GHCi> :t useRNG
useRNG
:: (MonadState s m, RandomGen s, Random a, Num a) =>
[a] -> a -> m [a]
...but when you use it, you haven't specified which concrete types you actually want.
If you disambiguate with a type signature:
test :: State StdGen [Int]
test = foldM useRNG [] [0 .. 50]
then it works fine. You could also accomplish this by putting a type signature on useRNG
:
useRNG :: [Int] -> Int -> State StdGen [Int]
Now, you might be thinking: if useRNG
works fine with all these types, why can't test
too? The answer is the monomorphism restriction, which is fairly arcane and not well-liked by many Haskell users. You can avoid it by either putting
{-# LANGUAGE NoMonomorphismRestriction #-}
at the top of your file, or giving test
an explicit type signature:
test :: (RandomGen g, Random a, Num a, Enum a, MonadState g m) => m [a]
You can find out the correct type signature with GHCi:
GHCi> :t foldM useRNG [] [0 .. 50]
foldM useRNG [] [0 .. 50]
:: (MonadState s m, RandomGen s, Random b, Num b, Enum b) => m [b]
(I wrote the explicit type signature before checking with GHCi, which is why mine is slightly different.)
However, this type signature is a little too polymorphic for practical uses — you'll basically be putting the ambiguity off until you actually use the result — so I'd suggest specifying it more concretely in this instance. You could, for instance, keep test
generic over the type of random number without the needless polymorphism over the state monad and generator type:
test :: (Random a, Num a, Enum a) => State StdGen [a]
You might also want to consider using MonadRandom, which wraps all the standard random number generation facilities in a state monad-based interface so you don't have to :)
Upvotes: 4