日食 fp - Haskell:交互使用导致错误



我正在尝试使用交互函数,但是以下代码遇到问题:

main::IO()
main = interact test
test :: String -> String
test [] = show 0
test a = show 3

我正在使用 EclipseFP 并接受一个输入,似乎有一个错误。尝试再次运行 main 会导致:

*** Exception: <stdin>: hGetContents: illegal operation (handle is closed)

我不确定为什么这不起作用,测试类型是字符串 -> 字符串,显示是显示 => -> 字符串,所以它似乎应该是交互的有效输入。

编辑/更新

我已经尝试了以下内容,它工作正常。使用取消行和行如何使交互按预期工作?

main::IO()
main = interact respondPalindromes
respondPalindromes :: String -> String
respondPalindromes =
    unlines .
    map (xs -> if isPal xs then "palindrome" else "not a palindrome") .
    lines
isPal :: String -> Bool
isPal xs = xs == reverse xs

GHCi 和不安全的 I/O

您可以将此问题(例外)简化为:

main = getContents >> return ()

interact电话 getContents

问题是stdingetContents确实hGetContents stdin)在对main的调用之间仍然在GHCi中进行评估。如果查找stdin,则实现为:

stdin :: Handle
stdin = unsafePerformIO $ ...

要了解为什么这是一个问题,您可以将其加载到 GHCi 中:

import System.IO.Unsafe                                                                                                           
f :: ()                                                                                                                           
f = unsafePerformIO $ putStrLn "Hi!"

然后,在 GHCi 中:

*Main> f
Hi!
()
*Main> f
()

由于我们已经使用了unsafePerformIO并告诉编译器f是一个纯函数,因此它认为不需要第二次计算它。在 stdin 的情况下,句柄上的所有初始化都不会第二次运行,并且它仍处于半关闭状态(hGetContents将其置于半关闭状态),这会导致异常。所以我认为 GHCi 在这种情况下是"正确的",问题在于stdin的定义,这对于只评估一次stdin的编译程序来说是一种实用的便利。

交互和延迟 I/O

至于为什么interact在一行输入后退出,而unlines . lines版本继续,让我们也尝试减少它:

main :: IO ()
main = interact (const "responsen")

如果您测试上述版本,交互甚至不会在打印response之前等待输入。为什么?以下是interact的来源(以GHC为单位):

interact f = do s <- getContents
                putStr (f s)

getContents是惰性I/O,并且由于在这种情况下f不需要s,因此不会从stdin读取任何内容。

如果将测试程序更改为:

main :: IO ()
main = interact test
test :: String -> String
test [] = show 0
test a = show a

您应该注意到不同的行为。这表明在你的原始版本(test a = show 3),编译器足够聪明,意识到它只需要足够的输入来确定字符串读取是否为空(因为如果它不为空,它不需要知道a是什么,它只需要打印"3")。由于输入大概在终端上进行了线路缓冲,因此它会一直读取,直到您按下回车键。

最新更新