Why does GHCI get "stuck" in an error state after an error?

303 views Asked by At

First of all, my apologies for the non-descriptive title. Since I have no idea what's actually going on I can't really make it any more specific.

Now for my question. I have implemented the following snippet for problem 23 of the 99 Haskell problems, which should randomly select n items from a list:

rndSelect' :: RandomGen g => [a] -> Int -> g -> ([a], g)
rndSelect' _ 0 gen = ([], gen)
rndSelect' [] _ _ = error "Number of items requested is larger than list"
rndSelect' xs n gen = ((xs !! i) : rest, gen'')
                    where (i, gen') = randomR (0, length xs - 1) gen
                          (rest, gen'') = (rndSelect' (removeAt xs i) (n - 1) gen')

rndSelectIO' :: [a] -> Int -> IO [a]
rndSelectIO' xs n = getStdRandom $ rndSelect' xs n

removeAt :: [a] -> Int -> [a]
removeAt xs n
  | length xs <= n || n < 0 = error "Index out of bounds"
  | otherwise = let (ys, zs) = splitAt n xs
                    in ys ++ (tail zs)

Now when I load this in ghci this works correctly for valid arguments:

*Main> rndSelectIO' "asdf" 2 >>= putStrLn 
af

However, strange things happen when I use an index that is out of bounds:

*Main> rndSelectIO' "asdf" 5 >>= putStrLn
dfas*** Exception: Number of items requested is larger than list
*Main> rndSelectIO' "asdf" 2 >>= putStrLn
*** Exception: Number of items requested is larger than list

As you can see, the following 2 (for me) unexpected things happen:

  1. Instead of directly giving an error it first print a permutation of the input.
  2. After it has given an error once, it won't execute at all anymore.

I suspect that 1. has to do with lazy evaluation, but I have absolutely no clue why 2. happens. What's going on here?

1

There are 1 answers

5
MathematicalOrchid On BEST ANSWER

The getStdRandom function basically looks up a StdGen value in a global variable, runs some function on it, puts the new seed back into the global variable, and returns the result to the caller.

If the function in question returns with an error, that error gets put into the global variable. Now all attempts to use this global variable will throw an exception. (I told you global variables are evil! ;-))

Try calling getStdGen manually yourself. It will either print out the current random seed, or throw an exception. If it throws an exception... there's your problem.

I believe you can use setStdGen to reset the thing.