我正在尝试使用交互函数,但是以下代码遇到问题:
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
)
问题是stdin
(getContents
确实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"
)。由于输入大概在终端上进行了线路缓冲,因此它会一直读取,直到您按下回车键。