2

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]’"
Will Ness
  • 70,110
  • 9
  • 98
  • 181
Vasiliy
  • 331
  • 1
  • 12
  • 3
    You should `fmap quicksort (randomList 10)`. – Willem Van Onsem Apr 20 '19 at 16:57
  • 5
    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. – Will Ness Apr 20 '19 at 16:59
  • @WillemVanOnsem, Yes, great, it works that way. – Vasiliy Apr 20 '19 at 17:10

1 Answers1

3

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))
Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • When compiling, I get an error in this line: "randomList :: (System.Random.Random t, Num t) => Int -> IO [t]" Not in scope: type constructor or class ‘System.Random.Random’ – Vasiliy Apr 20 '19 at 17:31
  • do `import System` as a whole, without the `(randomRIO)`. – Will Ness Apr 20 '19 at 17:32
  • Can I do the following afterwards? `sort n = do` `list <- randomList n` `let res = sortRandom list` `return res ` – Vasiliy Apr 20 '19 at 17:46
  • 1
    no. follow the types! (that's the general slogan of Haskell). go one by one, slowly, write down the types: `randomList n :: IO [Int]`; hence, `list :: [Int]`; `sortRandom :: (Monad m) => m [Int] -> m [Int]`, or specifically for `IO`, `sortRandom :: IO [Int] -> IO [Int]`. this means it is a function that expects `IO [Int]`, not just `[Int]`. So `list :: [Int]` can't be its argument. but we have `quicksort` which can accept the `[Int]` argument, so for `let res = quicksort list`, `res :: [Int]`. and so `return res :: IO [Int]`. I'll search for a link to an explanation of `do` shortly... – Will Ness Apr 20 '19 at 18:25
  • @Vasiliy, if you're working in `IO`, and with randomness, you should really use a genuine quicksort instead of that fake one! The only tricky thing is that you'll have to switch from lists to arrays. Of course, if you're not dedicated to quicksort, you should almost certainly use a different algorithm instead: bottom-up mergesort is very nice in Haskell, and heapsort is all right too. – dfeuer Apr 20 '19 at 18:32
  • @ WillNess, While there is no clear understanding of how this works. But you are helping me great! Thank you! – Vasiliy Apr 20 '19 at 19:19
  • @Vasiliy I try to explain the do notation [here](https://stackoverflow.com/a/44728131/849891). also check out [my other answers](https://stackoverflow.com/search?q=%5Bdo-notation%5D+user%3A849891) in the [tag:do-notation] tag. – Will Ness Apr 21 '19 at 07:29
  • @Vasiliy also, [this](https://stackoverflow.com/a/50815136/849891). so with do notation, the stuff to the right of `<-` lives in some monad `M` -- It can be `M a`,`M b` etc., but with the same `M` for every line in `do`. and if there's an `M a` type value to the right of `<-`, the value to its left has type `a`. that's the main thing. also, `do`s can be nested and unnested freely: `do { A ; B ; C } == do { A ; do {B ; C}} == do { do { A; B }; C }. if you have a question, don't hesitate to ask. – Will Ness Apr 21 '19 at 08:33