Reputation: 95
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
Upvotes: 4
Views: 7269
Reputation: 4360
choseRandom :: [a] -> IO a
choseRandom xs = (xs !!) <$> randomRIO (0, length xs - 1)
Upvotes: 0
Reputation: 48644
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
.
Upvotes: 4
Reputation: 9414
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)
Upvotes: 3
Reputation: 36329
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.
Upvotes: 7
Reputation: 144106
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))
Upvotes: 7