listhaskellbenchmarkingio-monadhaskell-criterion

Passing a randomly generated list as a parameter in Haskell


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)’

Solution

  • 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.)