How can I make a list of random numbers of type 'Double', that fit within a defined range? Info on this matter for a newbie like me is a little bit confusing. Trying something like:
randomlist :: Int -> Int -> [IO Double]
randomlist a b = do
g <- newStdGen
return (randomRs (a,b) g)
fails, with error:
Couldn't match expected type `[t0]' with actual type `IO StdGen'
Could you point to mistakes in my code?
You almost have it. You have two problems. The main problem is the [IO Double]
part of your type signature; this says you'll be returning a list of IO actions, each of which can produce a double. Instead, you want to return an IO [Double]
—an IO action which, when run, produces an infinite list of doubles. If you just change that, you're almost done; the remaining issue is that you have a
and b
as Int
s, but return Double
s. If you want to return doubles, your bounds need to be doubles, and similarly for integers. (To convert Int
s to Double
s, you can use fromIntegral
; to go the other way, you can use round
.) Thus, to get your code working, all you need to change is the type signature:
randomlist :: Double -> Double -> IO [Double]
randomlist a b = do
g <- newStdGen
return (randomRs (a,b) g)
And in fact, if you'd left off the type signature, everything would have been fine; GHC would have inferred the more general type signature Random a => a -> a -> IO [a]
. In other words, your function works with any data type you can generate random members of.
You can also simplify your code slightly. The following, for instance, is equivalent:
randomlist :: Random a => a -> a -> IO [a]
randomlist a b = fmap (randomRs (a,b)) newStdGen
The fmap :: Functor f => (a -> b) -> f a -> f b
function allows you to apply an ordinary function inside a functor. What's a functor? Roughly speaking, it's some sort of container; type functions such as []
, (r ->)
, and IO
are examples.1 This is exactly what you want; randomRs (a,b)
has type (Random a, RandomGen g) => g -> [a]
, and you instead need to give it something of type IO StdGen
, getting a Random a => IO [a]
back.
There's one more way you can make this nicer (and this is the way I'd write it). If you import Control.Applicative
, you end up with
import Control.Applicative
randomlist :: Random a => a -> a -> IO [a]
randomlist a b = randomRs (a,b) <$> newStdGen
<$>
is a synonym for fmap
; it looks like $
, ordinary application, because they're almost the same. <$>
just lifts you into a functor (here, IO
).
1: Don't worry if this isn't perfectly clear; you can get away with using this stuff without understanding it in full detail, which will eventually lead to understanding.
The main mistake is in your type signature. Removing it and asking ghci
what the inferred type is gives this:
*Main> :t randomlist
randomlist :: Random a => a -> a -> IO [a]
Of course, you may constrain this to the type Double -> Double -> IO [Double]
if you wish, and you can add some calls to fromIntegral
if you want to restrict further to integer bounds:
randomlist :: Int -> Int -> IO [Double]
randomlist a b = do
g <- newStdGen
return (randomRs (fromIntegral a, fromIntegral b) g)
Note the difference between the type [IO Double]
and IO [Double]
. The former is a list of computations returning Double
, while the latter is a single computation returning a list of Double
, which is what you want in this case.
The error message may be a bit cryptic, but it's basically telling you that because newStdGen
has the type IO StdGen
, the bind <-
is only allowed when the type of the do
-expression is an IO something
, whereas your type signature says the type should be [something]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With