i wonder can a IO() function return tuple because i would like to get these out of this function as input for another function.
investinput :: IO()->([Char], Int)
investinput = do
putStrLn "Enter Username : "
username <- getLine
putStrLn "Enter Invest Amount : "
tempamount <- getLine
let amount = show tempamount
return (username, amount)
Please help.
Thanks.
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
fis called with the argumentx, there must be no difference between calling it once, twice, or a hundred times. Consider what this means for IO, though. Naïvely,getLineshould have the typegetLine :: String, or perhapsgetLine :: () -> 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 wrotegetLine, it would have to return the same string, which is not what you want. This is the purpose of theIOtype: to encapsulate actions. These actions are distinct from functions; they represent impure computation (though they themselves are pure). A value of typeIO arepresents an action which, when executed, returns a value of typea. Thus,getLinehas typegetLine :: IO String: every time the action is evaluated, aStringis produced (by reading from the user). Similarly,putStrhas typeputStr :: 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 anIO (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: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
showintoread. Theshowfunction has the typeShow a => a -> String: it is a function which takes anything which can be shown and produces a string representing it. You wantedread, which has the typeRead 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 actionIO (String, Int). There is no pure way to do this; in other words, there is no pure functionIO a -> a. Why is this? BecauseIO arepresents an impure action which depends on the real world. If we had such a functionimpossibleRunIO :: IO a -> a, then we would want it to be the case thatimpossibleRunIO getLine == impossibleRunIO getLine, since the function must be pure. But this is useless, as we would wantimpossibleRunIOto be able to actually interact with the real world! Thus, this pure function is impossible. Everything that entersIOcan never leave. This is whatreturndoes: it is a function with, in this case1, the typereturn :: a -> IO a, which enables you to place pure values intoIO. For anyx,return xis an action which, when run, always producesx. This is why you have to end yourdoblock with thereturn:usernameis a pure value you extracted from an action, and as such is only visible within thedoblock. You need to lift it intoIObefore the outside world can see it. The same is true ofamount/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)
IOfront 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.