为什么 GHCI 在出错后会进入 "stuck" 错误状态?

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

首先,我对 non-descriptive 标题表示歉意。因为我不知道到底发生了什么,所以我真的不能让它更具体。

现在是我的问题。我已经为 99 Haskell problems 的问题 23 实现了以下代码片段,它应该从列表中随机 select 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. 与懒惰评估有关,但我完全不知道为什么 2. 会发生。这是怎么回事?

getStdRandom 函数基本上在全局变量中查找 StdGen 值,对其运行一些函数,将新种子放回全局变量中,然后 returns结果给调用者。

如果有问题的函数 returns 出现错误,该错误将被放入全局变量中。现在所有使用这个全局变量的尝试都会抛出异常。 (我告诉你们全局变量是邪恶的!;-)

尝试自己手动呼叫 getStdGen。它要么打印出当前的随机种子,要么抛出异常。如果它抛出异常...那就是你的问题。

我相信你可以使用 setStdGen 来重置它。