开发者

A list of random doubles within defined range in Haskell?

开发者 https://www.devze.com 2023-03-20 05:12 出处:网络
How can I make a list of random numbers of type \'Double\', that fit within a define开发者_如何转开发d range? Info on this matter for a newbie like me is a little bit confusing. Trying something like:

How can I make a list of random numbers of type 'Double', that fit within a define开发者_如何转开发d 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 Ints, but return Doubles. If you want to return doubles, your bounds need to be doubles, and similarly for integers. (To convert Ints to Doubles, 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].

0

精彩评论

暂无评论...
验证码 换一张
取 消