Почему GHCI "застревает" в состоянии ошибки после ошибки?

Прежде всего, мои извинения за не описательный заголовок. Поскольку я понятия не имею, что происходит на самом деле, я не могу сделать это более конкретным.

Теперь для моего вопроса. Я применил следующий фрагмент для проблемы 23 проблемы 99 Haskell, которая должна произвольно выбирать n элементы из списка:

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)

Теперь, когда я загружаю это в ghci, это корректно работает для допустимых аргументов:

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

Однако, странные вещи случаются, когда я использую индекс, который выходит за рамки:

*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

Как вы можете видеть, следующие 2 (для меня) неожиданно происходят:

  • Вместо того, чтобы прямо давать ошибку, сначала печатайте перестановку ввода.
  • После того, как он однажды дал ошибку, он больше не будет выполняться.

Я подозреваю, что 1. имеет отношение к ленивой оценке, но я совершенно не понимаю, почему 2. происходит. Что здесь происходит?

Ответ 1

Функция getStdRandom в основном ищет значение StdGen в глобальной переменной, запускает на нем некоторую функцию, помещает новое семя обратно в глобальную переменную и возвращает результат вызывающему.

Если возвращаемая функция возвращается с ошибкой, эта ошибка попадает в глобальную переменную. Теперь все попытки использовать эту глобальную переменную вызовут исключение. (Я сказал вам, что глобальные переменные являются злыми! ;-))

Попробуйте вручную вызвать getStdGen вручную. Он либо распечатает текущее случайное семя, либо выбросит исключение. Если он выдает исключение... там ваша проблема.

Я считаю, что вы можете использовать setStdGen для reset вещь.