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.