IO in Haskell doesn't work like IO in the languages you're used to. All functions in Haskell must be pure: that is, if a function f
is called with the argument x
, there must be no difference between calling it once, twice, or a hundred times. Consider what this means for IO, though. Naïvely, getLine
should have the type getLine :: String
, or perhaps getLine :: () -> String
. (()
is the unit type, whose only value is ()
; it's sort of like a void type in a C-like language, but there is a single value of it.) But this would mean that every time you wrote getLine
, it would have to return the same string, which is not what you want. This is the purpose of the IO
type: to encapsulate actions. These actions are distinct from functions; they represent impure computation (though they themselves are pure). A value of type IO a
represents an action which, when executed, returns a value of type a
. Thus, getLine
has type getLine :: IO String
: every time the action is evaluated, a String
is produced (by reading from the user). Similarly, putStr
has type putStr :: String -> IO ()
; it is a function which takes a string and returns an action which, when run, returns no useful information… but, as a side effect, prints something to the screen.
You are attempting to write a function of type IO () -> ([Char], Int)
. This would be a function which took as input an action and returned a tuple, which is not what you want. You want an IO (String, Int)
—an action which, when run, produces a tuple consisting of a string (which is a synonym for [Char]
) and an integer. You're almost there with your current code, too! This is what you'll need instead:
investinput :: IO (String, Int)
investinput = do
putStrLn "Enter Username : "
username <- getLine
putStrLn "Enter Invest Amount : "
tempamount <- getLine
let amount = read tempamount
return (username, amount)
Notice that I've only made two changes (and removed a blank line). First, I've changed the type of the function, like I said above. Second, I changed show
into read
. The show
function has the type Show a => a -> String
: it is a function which takes anything which can be shown and produces a string representing it. You wanted read
, which has the type Read a => String -> a
: given a string, it parses it and returns some readable value.
The other thing you asked about is returning a tuple (String, Int)
instead of an action IO (String, Int)
. There is no pure way to do this; in other words, there is no pure function IO a -> a
. Why is this? Because IO a
represents an impure action which depends on the real world. If we had such a function impossibleRunIO :: IO a -> a
, then we would want it to be the case that impossibleRunIO getLine == impossibleRunIO getLine
, since the function must be pure. But this is useless, as we would want impossibleRunIO
to be able to actually interact with the real world! Thus, this pure function is impossible. Everything that enters IO
can never leave. This is what return
does: it is a function with, in this case1, the type return :: a -> IO a
, which enables you to place pure values into IO
. For any x
, return x
is an action which, when run, always produces x
. This is why you have to end your do
block with the return
: username
is a pure value you extracted from an action, and as such is only visible within the do
block. You need to lift it into IO
before the outside world can see it. The same is true of amount
/tempamount
.
And just for completeness's sake: there is some overarching theory behind this which ties it together. But it's not necessary at all for beginning Haskell programming. What I would recommend doing is structuring most of your code as pure functions which fold, spindle, and mutilate your data. Then construct a thin (as thin as possible) IO
front layer which interacts with said functions. You'll be surprised how little IO you need!
1: It actually has a more general type, but that's not relevant for the moment.