Question

I recently started to learn Haskell. I'm trying to write a program that pick a random element of array:

import System.Random

randomInt :: (Int, Int) -> IO Int
randomInt range = randomRIO range :: IO Int

choseRandom :: [a] -> a
choseRandom list = 
    length list
    >>=
        (\l -> randomInt(0,l-1))
        >>=
            (\num -> (list !! num))

main :: IO ()
main = undefined

and I get the following error:

Build FAILED

C:\Users\User\Haskell\Real\src\Main.hs: line 7, column 9:
  Couldn't match expected type `IO Int' with actual type `Int'

    In the return type of a call of `length'

    In the first argument of `(>>=)', namely `length list'

    In the first argument of `(>>=)', namely

      `length list >>= (\ l -> randomInt (0, l - 1))'

what I'm doing wrong? It's hard for me to deal monads for first times

Was it helpful?

Solution

Since you're using IO inside choseRandom you need to change the type signature:

choseRandom :: [a] -> IO a

secondly, you don't need to use >>= to get the length of the list. >>= has type

Monad m => m a -> (a -> m b) -> m b

The type of length is [a] -> Int, so the type of length list is Int, which is not a monad.

You can calculate it directly when calling randomInt:

choseRandom :: [a] -> IO a
choseRandom list = 
    randomInt(0, length list) >>= (\num -> return (list !! num))

which is the same as

choseRandom :: [a] -> IO a
choseRandom list = fmap (\num -> (list !! num)) (randomInt(0, length list))

OTHER TIPS

It's hard for me to deal monads for first times

Yes, and you make it harder by avoiding syntactic support. Just write it thus:

choseRandom list = do
   let l = length list
   num <- randomInt(0,l-1)
   return (list !! num)

Doesn't this look much better?

Now to the point: the randomRIO function, as their type indicates, uses some global state (probably the system timer). Hence you can use results from RandomRIO only in the IO Monad.

An alternative would be to initialize a random generator in the main function, and pass this generator down to pure functions that need "random" values.

This should work:

import System.Random

randomInt :: (Int, Int) -> IO Int
randomInt range = randomRIO range :: IO Int

choseRandom :: [b] -> IO b
choseRandom list = randomInt (0, length list) >>= \x -> return $ list !! x

I find this more idiomatic:

choseRandom list = do
 a <- randomInt (0, length list)
 return $ list !! a

The problem with your choseRandom function is that the type signature is wrong there. The type of >>= should be m a -> (a -> m b) -> m b, so you should wrap the result back in the monad using return.

So it operates something like this:

randomInt (0, length list) will give you IO Int and using the >>= function you will extract out the Int from that. Now you can extract out the corresponding element from the list using !! function. But since the output type should be m b you shoud wrap it back in monad using return.

Firstly you need to fix the type signature fo choseRandom. Secondly I think you'll find this much easier if you use do notation.

choseRandom :: [a] -> IO a
choseRandom list = do
  let l = length list
  num <- randomInt (0, l-1)
  return (list !! num)
choseRandom :: [a] -> IO a
choseRandom xs = (xs !!) <$> randomRIO (0, length xs - 1)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top