tags:

views:

225

answers:

4

Hello to all, 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.

+4  A: 

Yeah, you're almost there but I think you want the signature:

investinput :: IO ([Char], Int)

... then from the calling function you can do something like:

main = do
    (username, amount) <- investinput
    ....

I think you want to read tempamount rather than show though.

Keith
Thanks it works but why this type signature is not working.investinput :: IO() -> ([Char], Int)
peterwkc
I need to return tuple rather than return IO tuple. nvestinput :: IO([Char], Int)investinput = do putStrLn "Enter Username : " username <- getLine putStrLn "Enter Invest Amount : " tempamount <- getLine let amount = read tempamount :: Int putStrLn "" return (username, amount)
peterwkc
I started trying to describe what's happening here using the IO box analogy, but I don't think a quick description is really helpful. There is no substitute for reading up on monads to understand why IO ([Char], Int) is the type. http://book.realworldhaskell.org/read/ is a great resource for this
Keith
+2  A: 

An IO function that produces a tuple would have type IO (a, b), in this case:

investinput :: IO ([Char], Int)

A signature of IO () -> ([Char], Int) would mean that the function takes a parameter of type IO () and produces a tuple from that, which is not what you want.

Generally there are no restrictions on the types an IO function (or a function in a different monad) can return, you can chose the types however you like.

sth
A signature of IO () -> ([Char], Int) would mean that the function takes a parameter of type IO () and produces a tuple from that, which is not what you want.I need a IO input and return tuple. Thanks. This is what i want.
peterwkc
No, `IO () -> ...` would mean that you give some IO function *as a parameter* to `investinput`, like calling it with `investinput (print 1)`. That your function uses another function like `getLine` which reads from the console is not reflected in the type signature. The type signature just states what types of parameters a function call expects and what type of return value it will produce.
sth
The reason i need the IO() -> ([Char, Int]) is because i have another function which expect a tuple to do the computation. I also saw some example like this lifM index (readFile "input.txt")liftM has this signature:liftM :: Monad m => (a -> b) -> m a -> m bIt takes a non-monadic function and transforms it into a monadic function.fmap index $ readFile "input.txt"readFile "input.txt" >>= return . indexPlease help.
peterwkc
@peterwkc: If the there is another function that needs a tuple, then this function should take a tuple as a parameter? How does that influence the parameters this function takes? Looking at the definition of the function in the question it doesn't take any parameters, so also its signature can't contain any parameters.
sth
The investinput should return tuple to another function after perform IO in investinput. Please help.Thanks.
peterwkc
+4  A: 

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.

Antal S-Z
Actually what i want is something like thistest = savefile investinput The invest input IO string is passes to savefile in order to saveinvestinput :: IO ([Char], Int)main = do (username, amount) <- investinputIf i do like this the main still have IO() type which is not i want. The test function should have different type.
peterwkc
I can't quite follow what you wrote, but I'll try to respond to it. You said that you don't want `main` to have the type `IO ()`. Let's think about what `main` is. It represents your whole program, so it must be some sort of impure action; and since it's your whole program, it can't return anything useful. Thus, it *must* have the type `IO ()`. And asking for it to have another type is like asking your C compiler to accept `double main(char* x, void (*y)())`—it's forbidden. If this isn't enough information yet, why don't you edit your question to explain what it is you're trying to do?
Antal S-Z
@Antal S-Z: Your point is of course correct, but as a bit of trivia, `main` is only required to have "type `IO t` for some type `t`" according to the report. So `main = getLine` is a valid (and useless) program. This is completely irrelevant to peterwkc's question, but it's a funny little fact that surprised me when I came across it.
Travis Brown
+1  A: 

The answer to your question about returning (String, Int) rather than IO (String, Int) is simple: you can't. Once you're in IO you're stuck there. That's part of what it means when people say that Haskell is a "pure" language.

What you want to do is similar to what you're already doing here with getLine. The type of getLine is IO String. When you write username <- getLine, you're in effect taking the String out of the IO String, but this is only possible because you're inside the do expression.

You can do exactly the same kind of thing with investinput as with getLine. Here's an example of how you could use investinput in your main function:

main = do
  (name, amount) <- investinput
  putStrLn $ name ++ " invested $" ++ show amount ++ "."

Since you mention liftM in a comment, here's a full working version that does the same thing using liftM and the reverse bind operator (=<<) instead of do notation:

import Control.Monad (liftM)

investinput :: IO (String, Int)
investinput = do
   putStrLn "Enter Username : "
   username <- getLine
   putStrLn "Enter Invest Amount : "
   amount <- liftM read getLine -- We can also use liftM to get rid of tempamount.
   return (username, amount)

summary :: (String, Int) -> String
summary (name, amount) = name ++ " invested $" ++ show amount ++ "."

main = putStrLn =<< liftM summary investinput

This shows how you could use investinput with "another function which expects a tuple".

Travis Brown
I try the code but it is not compile. import Monad-- Receive IO input do somethingsummary :: ([Char], [Char]) -> Stringsummary (name, amount) = putname ++ " invested $" ++ amount ++ "."investinput :: IO ()investinput = do putStrLn "Enter Username : " username <- getLine putStrLn "Enter Invest Amount : " tempamount <- getLine putStrLn "" main = putStrLn =<< liftM summary investinput
peterwkc
I've edited to include a full compilable example.
Travis Brown
Thanks. Can you explain these lines of code ? liftM summary investinputWhat is liftM ? It say promote a function into monad. What this mean ?
peterwkc
I want output of maxinvestinput as input for maximuminvest. Thanks. maxinvest = liftM maximuminvest maxinvestinputmaxinvestinput :: IO()maxinvestinput = dostr <- readFile "C:\\Invest.txt"let cont = words strlet mytuple = converttuple contlet myint = getint mytupleputStrLn ""converttuple :: [String] -> [(String, Integer)]converttuple (x:y:z) = (x, read y):converttuple zgetint :: [(String, Integer)] -> [Integer]getint (x:xs) = snd (x) : getint xsmaximuminvest :: (Ord a) => [a] -> amaximuminvest (x:xs)| x > maxTail = x| otherwise = maxTailwhere maxTail = maximuminvest xs
peterwkc