-1

I would like to generate random numbers in a range and the type signature to be Int -> Int. I've read multiple other posts but none of them suggested ways to return a type Int. I used System.IO.Unsafe in my code but it is not recommended to do so. Here's my code:

import System.IO.Unsafe

-- random number generator
rng :: Int -> Int
rng upper = unsafePerformIO $ randomRIO (0,upper-1) 

Does anyone have any suggests on how to generate random Int in a range in Haskell?

Edit: It might be impossible to change IO Int -> Int so I converted my code to

-- random number generator
rng :: Int -> IO Int
rng upper = randomRIO (0,upper-1) 

The reason why I need a rng is because I want to get random numbers within the range length of the list to get an index for an element of a list.

list !! rng (length list) but I'm getting the error Couldn't match expected type ‘Int’ with actual type ‘IO Int’ which is expected.

It's not a duplicate because 1. I want values in a range, 2. my rng does not return the same values. I'm new to Haskell and I don't know how to manipulate Monads. Any help is appreciated.

llamaro25
  • 642
  • 1
  • 7
  • 22
  • 4
    You can't sensibly make it `Int -> Int`. The closest you can get and still behave sensibly is `Int -> StdGen -> (StdGen, Int)`; this type may be implemented using `randomR` rather than `randomRIO`. – Daniel Wagner Oct 01 '19 at 05:13
  • 3
    Possible duplicate of [How can I generate different random values in Haskell?](https://stackoverflow.com/questions/57836652/how-can-i-generate-different-random-values-in-haskell) – Peter O. Oct 01 '19 at 05:22
  • 1
    `rng x = fst $ randomR (0, x-1) $ makeStdGen $ length "https://xkcd.com/221"` :P – HTNW Oct 01 '19 at 05:23
  • You do not need to involve IO, but because of Haskell _referential transparency_ you cannot have that `Int -> Int` type signature. [See here for a more detailed explanation](https://stackoverflow.com/a/57890878/11282404). – jpmarinier Oct 01 '19 at 16:28
  • You can also use the techniques mentioned here: https://stackoverflow.com/q/6311512 . – atravers Sep 26 '21 at 09:04

2 Answers2

7

I the spirit of https://xkcd.com/221/, here's a “solution” without any IO:

rng :: Int -> Int
rng upper
 | upper<=4   = upper
 | otherwise  = 4

So that gives you an “RFC 1149.5 compliant random number”. It's always four, unless that is outside the range.

What's the problem with this? Well, clearly it gives always the same number – and so it must be, because all Haskell functions must be functions, i.e. referentially transparent. OTOH, a random number generator is supposed to give different number each time you call it... it is thus not a function, and most other programming languages merely pretend it is a function with side-effect – because they have no proper means to express what side-effects are. Well, Haskell does have a proper means of expressing that, and it is the IO monad: you can have computations that depend on a side effect, but clearly these computations if you run them will then have that side-effect themselves.
In that light, the signature Int -> IO Int does make sense for the function. (This is a function, but the result is an IO action and only executing that action gives you an Int.)

What's ugly about that is that IO Int could literally do anything in IO – it could, for instance, launch some missiles and give you back the number of casualities. More realistically, it could easily modify some file in you home directory. Whereas what you want is actually just a teeny tiny harmless side-effect, just enough to produce a new random number the next time. Usually, random number generators are anyways not really random but PRNGs, which keep a constant-size state variable that is updated in a random-looking way each time you pull a value. The next time, this state will be different and thus you get a different value, as desired. This state variable could be held in an IO-mutable location

import Data.IORef

type RandStV = Int
type RandSt = IORef RandStV

rng' :: RandSt -> Int -> IO Int
rng' rSt upper = do
   x <- readIORef rSt
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff -- https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/random_r.c;hb=glibc-2.26#l362
   writeIORef rSt x'
   return $ x `mod` upper

...or, you could just explicitly pass the updated state along with the result

rng'' :: Int -> RandStV -> (RandStV, Int)
rng'' upper x =
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
   in (x', x `mod` upper)

...or it could be passed around in a dedicated state monad, which is just another way of writing the passing-on of an updated variable:

type RandStM = State RandStV

rng''' :: Int -> RandStM Int
rng''' upper = do
   x <- get
   let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
   put x'
   return $ x `mod` upper

See the random-fu package for useful helpers on such a random monad.

One mathematical way to interpret rng''' is to say it is a function that takes an upper bound as the argument and gives you back a distribution of numbers. The distribution is always the same, but it “contains” many numbers together with the probability of them occuring. Actually generating an integer means you're sampling from the distribution.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
2

Haskell was not built to generate random numbers without using IO.

Your example, list !! rng (length list), doesn't work because rng returns IO Int and !! expects a Int.

Here is a function that uses your rng function to get a random element from a list:

-- Will crash on empty list
randomElementFromList :: [a] -> IO a
randomElementFromList list = do
  r <- rng (length list)
  return $ list !! r
Garrison
  • 386
  • 2
  • 8
  • 1
    No reason to specify this down to `[Int]`. `randomElementFromList :: [a] -> IO a` – Adam Smith Oct 01 '19 at 05:32
  • And of course the pattern `do { x <- m; return (f x) }` is equivalent to `fmap f m` / `f <$> m`, e.g.: `(list !!) <$> rng (length list)`. When I was learning Haskell, picking up these patterns helped me understand the functor/applicative/monad operators, as “abbreviations” of patterns in `do` notation (or, in another sense, just different kinds of application and composition). – Jon Purdy Oct 04 '19 at 01:07