Search code examples
haskelltype-mismatchio-monaddo-notation

Why can't I call a function quicksort (randomList 10)?


I have the following code:

import Data.Array
import Control.Monad
import Data.Functor 
import System.Random (randomRIO)

randomList 0 = return []
randomList n = do
  r  <- randomRIO (1,6)
  rs <- randomList (n-1)
  return (r:rs) 

quicksort [] = []  
quicksort (x:xs) =   
    let smallerSorted = quicksort [a | a <- xs, a <= x]  
        biggerSorted = quicksort [a | a <- xs, a > x]  
    in  smallerSorted ++ [x] ++ biggerSorted  
  1. randomList - creates a list of a given length and populates it with random values;
  2. quicksort - quickly sorts the list.

I need to apply sorting to the created array: quicksort (randomList 10), but an error occurs:

"Couldn't match expected type‘ [a] ’with actual type IO [Int]’"

Solution

  • You should include type signatures for all top-level names in your program. if you don't know them, load the file and ask GHCi: Main> :t randomList. Then copy-paste it to the file (or specialize it first, as you see fit). Place the type signature above the name it describes.

    GHCi says

    randomList ::
      (System.Random.Random t, Num t, Num a, Eq a) => a -> IO [t]
    

    but you most probably meant

    randomList :: (System.Random.Random t, Num t) => Int -> IO [t]
    randomList 0 = return []
    randomList n = do
      r  <- randomRIO (1,6)    -- randomRIO (1,6) :: IO t  , r :: t
      rs <- randomList (n-1)   --             rs :: [t]
      return (r:rs)            --    r :: t , rs :: [t] 
    

    In general,

    randomRIO (1,6) :: (System.Random.Random a, Num a) => IO a
    

    You cast a 6-sided die n times and collect the results in a list. By the way the same thing is done by

    sequence $ replicate n (randomRIO (1,6))
    ===
    replicateM n (randomRIO (1,6))
    
    > :t \n -> replicateM n (randomRIO (1,6))
               :: (System.Random.Random a, Num a) => Int -> IO [a]
    

    Then, GHCi also tells us that

     quicksort :: Ord t => [t] -> [t]
    

    But randomList n is IO [t], not [t]. To get to the [t] value living inside the IO [t], you need to do it on the inside:

    sortRandom :: (Ord t, Monad m) => m [t] -> m [t]
    sortRandom randomlist = do
        xs <- randomlist        -- randomlist :: IO [t] , xs :: [t]
        let ys = quicksort xs
        return ys
    

    The above can be abbreviated to

    sortRandom :: (Ord t, Monad m) => m [t] -> m [t]
    sortRandom = liftM quicksort     -- for monads
    

    or

    sortRandom :: (Ord t, Functor f) => f [t] -> f [t]
    sortRandom = fmap quicksort      -- for functors
    

    whichever you prefer. Both work with IO which is a monad, and any monad is also a functor. So in the end you can define

    foo :: Int -> IO [Int]
    foo n = liftM quicksort $ replicateM n (randomRIO (1,6))