如何在带有命令行参数的 Haskell 程序中读取标准输入?

How to read stdin in a Haskell program with command-line arguments?

我知道我可以使用 interact :: (String -> String) -> IO () 在一个简单的 Haskell 程序中方便地读取标准输入并写入标准输出(参见 http://learnyouahaskell.com/input-and-output)。

现在我想添加命令行参数来制作我的简单程序 "configurable"。

有没有办法做到这一点并且仍然使用 interact(以便以最小的努力获得可配置的程序)?

这可以通过 "sequencing" IO 计算完成 do 并在使用 interact 之前使用 getArgs:

import System.Environment (getArgs)
main :: IO ()
main = do
    args <- getArgs
    interact (<whatever using args>)

在大多数程序中,interact 很快就会被 getContentsputStr 替代。这是因为 interact 的局限性很大,您只能调用一个函数,并且该函数必须消耗所有输入。随着程序复杂性的增加,您最终会希望将程序分解为更小的函数,一次处理一部分输入,而 do 表示法使得对这些函数进行排序变得更加容易。

因此,为了模仿 interact 但也使用命令行参数,您最初可能会这样做:

import System.Environment

doStuff :: [String] -> String -> String
doStuff args input = undefined -- your code here

main :: IO ()
main = do
    args <- getArgs
    contents <- getContents
    putStr (doStuff args contents)

顺便说一下,这与:

main = doStuff <$> getArgs <*> getContents >>= putStr

但稍后您可能希望添加诸如提示、解析器或文件之类的东西 I/O:

import System.Environment

data Arg = Taco | Boring

parseArg :: String -> Arg
parseArg arg = if arg == "taco" then Taco else Boring

doStuff :: Arg -> String -> String
doStuff Taco   input = "Yum, tacos! " ++ input
doStuff Boring input = "Meh, " ++ input

main :: IO ()
main = do
    [arg] <- map parseArg <$> getArgs
    putStr "Enter the file name: "
    fileName <- getLine
    fileContents <- readFile fileName
    writeFile ("output-" ++ fileName) (doStuff arg fileContents)