Haskell:交互使用导致错误 [英] Haskell: Interact use causing error

查看:83
本文介绍了Haskell:交互使用导致错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  main 

我试图使用交互功能,但遇到以下代码的问题: :: IO()
main =交互测试

test :: String - > String
test [] = show 0
test a = show 3

I使用EclipseFP并采取一个输入似乎有错误。尝试再次运行main会导致:

  ***例外:< stdin>:hGetContents:非法操作关闭)

我不知道为什么这不起作用,测试的类型是String - > String和show是Show a => a - > String,所以它应该是一个有效的交互输入。



我已经尝试了以下,它工作正常。

  main :: IO()
main =交互respondPalindromes

respondPalindromes ::字符串 - >字符串
respondPalindromes =
unlines。
map(\ xs - > if isPal xs thenpalindromeelsenot a palindrome)。


isPal :: String - > Bool
isPal xs = xs ==反向xs


解决方案

< h2> GHCi和不安全的I / O

您可以将此问题(例外)减少到:

  main = getContents>> return()

interact calls getContents



问题在于 stdin getContents 确实是 hGetContents stdin )仍然在GHCi中调用 main 。如果您查找 stdin ,它将实现为:

  stdin ::处理
stdin = unsafePerformIO $ ...

要明白为什么这是一个问题,您可以加载到GHCi中:

  import System.IO.Unsafe 

f ::()
f = unsafePerformIO $ putStrLn嗨!

然后,在GHCi中:

  *主> f 
嗨!
()
* Main> f
()

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

互动和延迟I / O



至于为何交互在单行输入后退出,而退出。

$ b main = interact(constresponse \ n)

如果您测试上述版本,在打印响应之前,交互甚至不会等待输入。为什么?这是 interact (在GHC中)的源代码:

  interact f = s<  -  getContents 
putStr(fs)

getContents 是懒惰的I / O,因为在这种情况下 f 不需要 s ,没有任何内容从 stdin 读取。



如果您将测试程序更改为:

  main :: IO()
main =交互测试

test :: String - >字符串
test [] =显示0
测试a =显示

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


I'm trying to use the interact function, but I'm having an issue with the following code:

main::IO()
main = interact test

test :: String -> String
test [] = show 0
test a = show 3

I'm using EclipseFP and taking one input it seems like there is an error. Trying to run main again leads to a:

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

I'm not sure why this is not working, the type of test is String -> String and show is Show a => a -> String, so it seems like it should be a valid input for interact.

EDIT/UPDATE

I've tried the following and it works fine. How does the use of unlines and lines cause interact to work as expected?

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 and Unsafe I/O

You can reduce this problem (the exception) to:

main = getContents >> return ()

(interact calls getContents)

The problem is that stdin (getContents is really hGetContents stdin) remains evaluated in GHCi in-between calls to main. If you look up stdin, it's implemented as:

stdin :: Handle
stdin = unsafePerformIO $ ...

To see why this is a problem, you could load this into GHCi:

import System.IO.Unsafe                                                                                                           

f :: ()                                                                                                                           
f = unsafePerformIO $ putStrLn "Hi!"

Then, in GHCi:

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

Since we've used unsafePerformIO and told the compiler that f is a pure function, it thinks it doesn't need to evaluate it a second time. In the case of stdin, all of the initialization on the handle isn't run a second time and it's still in a semi-closed state (which hGetContents puts it in), which causes the exception. So I think that GHCi is "correct" in this case and the problem lies in the definition of stdin which is a practical convenience for compiled programs that will just evaluate stdin once.

Interact and Lazy I/O

As for why interact quits after a single line of input while the unlines . lines version continues, let's try reducing that as well:

main :: IO ()
main = interact (const "response\n")

If you test the above version, interact won't even wait for input before printing response. Why? Here's the source for interact (in GHC):

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

getContents is lazy I/O, and since f in this case doesn't need s, nothing is read from stdin.

If you change your test program to:

main :: IO ()
main = interact test

test :: String -> String
test [] = show 0
test a = show a

you should notice different behavior. And that suggests that in your original version (test a = show 3), the compiler is smart enough to realize that it only needs enough input to determine if the string read is empty or not (because if it's not empty, it doesn't need to know what a is, it just needs to print "3"). Since the input is presumably line-buffered on a terminal, it reads up until you press the return key.

这篇关于Haskell:交互使用导致错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆