如何将用户输入作为字符串返回而不是将其打印出来或在Haskell中获取IO字符串?

时间:2014-10-31 19:09:38

标签: haskell io

以下是我目前的代码:

type Deck = [Card]
data Card = Card {question :: String, answer :: String} deriving (Show)

askForNextCommand deck = do
    putStrLn "What would you like to do?"
    userInput <- getLine
    return userInput

loop :: Deck -> IO ()
loop deck = print $ askForNextCommand deck

main :: IO ()
main = loop []

我遇到的问题是askForNextCommand功能。我希望能够在另一个函数中使用用户输入,如下所示:
有一副卡片,每个卡片都包含一个问题和答案,用户可以对它们进行测验,在列表中添加更多卡片,删除卡片等。

当我学习它时,我在Python中创建了一个类似的程序,所以我现在正尝试在Haskell中创建它。

我有另一个函数接受输入并根据输入的内容做一些事情:

doCommand command deck
    | command == "add" = addFunc deck
    | command == "remove" = removeFunc deck
    | otherwise = doCommand (askForNextCommand deck) deck

问题是我无法弄清楚如何将命令参数作为用户输入。我希望askForNextCommand提示用户然后将其输入作为字符串返回,但我现在已经搜索了大约半小时但找不到任何内容。我确定这是一个简单的修复,但我不知道在哪里看。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

首先,始终为您的顶层绑定提供类型签名。这将极大地帮助您解决类型错误。

askForNextCommand :: Deck -> IO String
askForNextCommand deck = do
    putStrLn "What would you like to do?"
    userInput <- getLine
    return userInput

顺便说一句,最后两行是反模式。编写上述函数的标准方法是:

askForNextCommand :: Deck -> IO String
askForNextCommand deck = do
    putStrLn "What would you like to do?"
    getLine

到目前为止一切顺利。现在到了罪魁祸首:

loop :: Deck -> IO ()
loop deck = print $ askForNextCommand deck

此处askForNextCommand deck的类型为IO String(请参阅上一个签名)。 函数print尝试将其转换为字符串(从技术上讲,转换为类型类Show), 但为此,它需要一个不可能构建的函数show :: IO String -> String

实际上,IO String -> String会神奇地转换与用户交互的一段代码,并在一段代码中产生刺痛(例如getLine),从而产生没有用户交互的字符串。 getLine中提取字符串,因此这是不可能的。

以下是更正后的版本:

loop :: Deck -> IO ()
loop deck = do
     cmd <- askForNextCommand deck
     print cmd

或等同地

loop :: Deck -> IO ()
loop deck = askForNextCommand deck >>= print

上面运行IO,获取字符串,然后打印它。 print现在收到String,所以一切都很好。

拇指规则是:您可以使用x <- someIOValue内的do从IO monad中提取内容。问题是您只能在再次返回IO类型的函数中执行此操作。

如果你想了解更多关于Haskell的monads和朋友的信息,那就是优秀的博文 Functors, Applicatives, And Monads In Pictures在实践中解释了它们 轻量级的方式。

最后,上面的代码会添加一些额外的引号。使用putStrLn代替print是否要避免引用。