我正在尝试使用交互功能,但我遇到以下代码的问题:
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)
我不确定为什么这不起作用,测试类型是String - &gt;字符串和显示是Show a =&gt; a - &gt;字符串,所以它似乎应该是交互的有效输入。
修改/更新
我尝试了以下内容并且工作正常。如何使用unlines和lines导致交互按预期工作?
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
答案 0 :(得分:7)
您可以将此问题(例外)减少为:
main = getContents >> return ()
(interact
来电getContents
)
问题在于stdin
(getContents
确实是hGetContents stdin
)仍然在GHCi之间对main
的调用进行评估。如果您查找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
一次的编译程序来说是一种实用的便利。
至于为什么interact
在unlines . lines
版本继续进行单行输入后退出,让我们尝试减少它:
main :: IO ()
main = interact (const "response\n")
如果您测试上述版本,则在打印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"
)。由于输入可能是在终端上进行行缓冲,因此它会一直读取,直到您按下返回键。