简体   繁体   中英

How to return user input as a string instead of printing it out or getting an IO String in Haskell?

Below is the code I have so far :

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 []

The problem I'm having is with the askForNextCommand function. I'd like to be able to use the user input in another function like this :
There's a deck of cards, each of which contain a question and answer, and the user can be quizzed on them, add more cards to the list, remove cards, etc.

I've made a similar program in Python when I was learning it so I'm trying to make it in Haskell now.

I have another function that takes the input and does something with it depending on what the input is :

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

The problem is I can't figure out how to get the command parameter to be user input. I'd like askForNextCommand to prompt the user then return their input as a string, but I've been searching for about a half hour now and can't find anything. I'm sure it's an easy fix but I'm not sure where to look. Any help would be greatly appreciated.

First, always provide a type signature for your toplevel bindings. This will help you greatly in figuring type errors out.

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

By the way, the last two lines are an antipattern. The standard way to write the function above would be:

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

So far so good. Now to the culprit:

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

Here askForNextCommand deck is of type IO String (see the previous signature). Function print attempts to convert it to a string (technically to the type class Show ), but for that it would need a function show :: IO String -> String which is impossible to construct.

Indeed, an IO String -> String would magically convert a piece of code that interacts with the user and produces a sting (eg getLine ) into a piece of code which produces the string with no user interaction. We can not extract the string from getLine without actually doing the IO, so this is impossible.

Here's the corrected version:

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

or, equivalently,

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

The above runs the IO, takes the string, and then prints it. print receives a String now, so everything is fine.

The thumb rule is: you can extract things from the IO monad using x <- someIOValue inside a do . The catch is that you can do that only in a function which returns an IO type again.

If you want to learn more about monads and friends in Haskell, there's the excellent blog post Functors, Applicatives, And Monads In Pictures which explains them in a practical and lightweight way.

Finally, the code above will add some extra quotes. Use putStrLn instead of print is you want to avoid quotes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM