Haskell - 我如何打破互动?

Haskell - How do I break out of interact?

我正在使用 interact 逐步处理一些用户输入(具体来说,这是一个国际象棋程序)。但是,我还没有找到一种方法来处理用户可能只想跳出循环并从头开始这场国际象棋比赛的情况。

当我在 ghci 中执行正常程序时,按 Ctrl-C 不会退出整个 ghci,只会停止程序本身并允许我继续执行其他一些程序。但是,如果我在控制台中按 Ctrl-C 并使 interact 功能生效,则会显示以下消息:

^CInterrupted.
*Main> 
<stdin>: hGetChar: illegal operation (handle is closed)

然后我必须重新启动 ghci。

我也想过捕捉特殊的用户输入,例如 "exit",但是,由于 interact 的类型是 interact :: (String -> String) -> IO (),输入必须通过函数类型 (String -> String) 首先,我还没有找到让该函数通知主 IO 它应该退出的方法。

我该如何突破interact?还是 interact 不打算以这种方式使用,我应该编写自定义 IO 函数?

How should I break out of interact?

你不能。您可以将 interact f 视为 getContents >>= putStrLn . fgetContents 将关闭 stdin 上的句柄。任何进一步的读取操作都会失败。

The literal character ^D gets shown in the terminal

这是 readline 的问题。 GHCi 将 stdin 的缓冲方法从 LineBuffer 更改为 NoBuffering 以优化使用 readline。如果要用^D退出interact,需要改变缓冲方式:

ghci> import System.IO
ghci> hGetBuffering stdin
NoBuffering
ghci> hSetBuffering stdin LineBuffering
ghci> interact id
hello world
hello world
pressing control-D after the next RETURN
pressing control-D after the next RETURN
<stdin>: hGetBuffering: illegal operation (handle is closed)

Or is interact not intended to be used this way and I should compose custom IO functions?

是的,它不适合以这种方式使用。 interact 意味着使用所有输入并指示所有输出。如果你想使用line-wise input,你可以自己写line-wise interact方法(或者使用外部库):

import Control.Monad (when)

interactLine :: (String -> String) -> IO ()
interactLine f = loop
  where
    loop = do
      l <- getLine
      when (l /= "quit") $ putStrLn (f l) >> loop