2

Good evening everybody! This is a question concerning Haskell. I want to get x random elements from a list by using a function.

The problem I get is that when I try to use random numbers, with randomRIO. I get the error message:

No instance for (Control.Monad.IO.Class.MonadIO [])
        arising from a use of `randomRIO'

This error message suddenly goes away when i use print or return. But I dont want to use print, and return messes up the output to a nested list [[a]] instead of [a].

Does any of you have a tip on what I can do to extract x random elements from the list, in the form of a list?

The type would be something like this. xRanElems :: [a] -> Int -> [a] where the second Int is an accumulator but I got that covered.

xRanElems xs n = do
  r <- randomRIO (0, n)
  xRanElems2 xs n r

where n is just length xs - 1

xRanElems2 xs n r = (xs !! r) : (xRanElems (xsWithOutSelectedElem xs r) (n-1))

Thankful for any input!

Will Ness
  • 70,110
  • 9
  • 98
  • 181
SnusSwag
  • 23
  • 4
  • 1
    `return $ xRanElems2 xs n r`? – arrowd Feb 22 '22 at 14:59
  • Thank you, Yes I tried that but it caused problems because xRanElems2 is recursive I believe. It tells me that it expects [a] but actual is [[a]] – SnusSwag Feb 22 '22 at 15:16
  • And I think return does something with the the list that messes it up in some way. – SnusSwag Feb 22 '22 at 15:17
  • 2
    As usual: if you're doing `IO`, you have to admit it. Write `xRanElems :: [a] -> Int -> IO [a]` and your error messages will be much more informative. (Similarly for `xRandElems2`, but changing one will give you errors about the other to remind you.) – Daniel Wagner Feb 22 '22 at 15:27
  • Oh okay, I will try that, so a random generated number is of IO class? Thanks – SnusSwag Feb 22 '22 at 15:30
  • It works kind of, but then reaches the second problem i mentioned. Namely that it complains that it expexts [a] and not [[a]]. I dont know how it even made it into [[a]] but I think it has something do do with return – SnusSwag Feb 22 '22 at 15:40
  • @SnusSwag: “a random generated number is of IO class?” Close, but not exactly. You can use an `IO` action to produce a random number, for example, if you want an action that produces a new random `Int` from 1 to 6 each time it’s executed, you could define `rollD6 :: IO Int; rollD6 = randomRIO (1, 6)`. You can combine `IO` actions using `do`/`Functor`/`Applicative`/`Monad` functions, and assign them to `main` to be executed, but an `IO X` isn’t an `X`, and it doesn’t even *contain* an `X`, it just represents some code that could *produce* an `X` value. – Jon Purdy Feb 22 '22 at 23:33
  • Mm okay I get it now, I have seen something similar during a recorded lecture but it didn't stick. Thanks for your comment! – SnusSwag Feb 23 '22 at 07:39

1 Answers1

4

The following typechecks:

import System.Random

xRanElems  :: [a] -> Int -> IO [a]
xRanElems xs n = do
  -- randomRIO :: Random t 
  --           => (t, t) -> IO    t
  r <- randomRIO  (0, n)  -- r :: Int
  xRanElems2  xs      n      r    

xRanElems2 :: [a] -> Int -> Int -> IO [a]
xRanElems2 xs n r = 
   let (a,b:c) = splitAt r xs 
   in 
     fmap (b :)                --     [a] ->    [a]
          (xRanElems           --  IO [a] -> IO [a]
               (a++c) (n-1))

Trying to run it, e.g. with xRanElems [1..3] 2, reveals that it loops forever.

This is because you need to provide the base case in xRanElems to stop the recursion, e.g. returning [] when n <= 0.

The above code also contains an off-by-1 error which you're invited to fix.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • splitAt was new for me. fmap solved the issue! That blew my mind. I got the other problems taken care of. Thank you, have a good dinner! – SnusSwag Feb 22 '22 at 17:05