I am new to Haskell and really having trouble with the whole IO thing.
I am trying to find out how long it takes to traverse a list in haskell. I wanted to generate a list of random numbers and pass it as a parameter to a function so that I can print each element of the list. I am using CRITERION package for the benchmark. Here is the code:
{-# LANGUAGE OverloadedStrings #-}
import System.Random
import Control.Exception
import Criterion.Main
printElements [] = return ()
printElements (x:xs) = do print(x)
printElements xs
randomList 0 = return []
randomList n = do
x <- randomRIO (1,100)
xs <- randomList (n-1)
return (x:xs)
main = defaultMain [
bgroup "printElements" [ bench "[1,2,3]" $ whnf printElements (randomList 10)
, bench "[4,5,6]" $ whnf printElements [4,5,6,4,2,5]
, bench "[7,8,9]" $ whnf printElements [7,8,9,2,3,4]
, bench "[10,11,12]" $ whnf printElements [10,11, 12,4,5]
]
]
Error when I run the code:
listtraversal.hs:18:67:
Couldn't match expected type ‘[a0]’ with actual type ‘IO [t0]’
In the second argument of ‘whnf’, namely ‘(randomList 10)’
In the second argument of ‘($)’, namely
‘whnf printElements (randomList 10)’
Put briefly, you need to bind your function to the IO
value, instead of trying to apply it to the value wrapped inside the IO
value.
-- instead of whnf printElements (randomList 10)
randomList 10 >>= whnf printElements
randomList
does not return a list of values; it returns an IO
action that, when executed, can produce a list of values. Ignoring the various constraints induced by the implementation, the type is
randomList :: (...) => t1 -> IO [t] -- not t1 -> [t]
As such, you can't directly work with the list of values that the IO
action can produce; you need to use the monad instance to bind the value to an appropriate function. whnf printElements
is one such function; it takes a list and returns an IO
action.
whnf printElements :: Show a => [a] -> IO ()
Instead of pulling the list out and passing it to whnf printElements
, we "push" the function into an IO
value using >>=
. That operator's type, specialized to the IO
monad, is
(>>=) :: IO a -> (a -> IO b) -> IO b
In this case, the first IO a
value is the IO [t]
value returned by randomList
. whnf printElements
is the a -> IO b
function we bind to.
The result is a new IO
value that take the first IO
value, pulls out the wrapped value, applies the given function, and returns the result.
In other words, the IO
monad itself takes care of pulling apart the result from randomList
and applying your function to it, rather than you doing it explicitly.
(You might have noticed that I've said that >>=
binds a value to a function and vice versa. It is perhaps more accurate to say that >>=
binds them together into a single IO
action.)