Haskell Return Tuple

I am wondering if the returned tuple of the IO () function can, because I would like to get them from 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
source share
4 answers

Haskell's IO does not work like IO in the languages ​​you're used to. All functions in Haskell must be clean: that is, if the function f is called with the argument x , there should be no difference between its call one, two or a hundred times. However, consider what this means for IO. Naive, getLine should be of type getLine :: String or, possibly, getLine :: () -> String . ( () is a single type whose only value is () , it is similar to the void type in C-type language, but there is its only value.) But this will mean that every time you wrote getLine , it should have would return the same string, which is not what you want. This is an IO type goal: encapsulate actions. These actions differ from functions; they are impure calculations (although they themselves are clean). A value of type IO a is an action that, when executed, returns a value of type a . Thus, getLine is of type getLine :: IO String : every time an action is evaluated, a String is created (by reading from the user). Similarly, putStr is of type putStr :: String -> IO () ; this is a function that takes a string and returns an action that when launched does not return any useful information ... but, as a side effect, displays something on the screen.

You are trying to write a function like IO () -> ([Char], Int) . This would be a function that takes action as input and returns a tuple that is not what you want. You want to perform an IO (String, Int) -an action, which when launched creates a tuple consisting of a string (which is a synonym for [Char] ) and an integer. You are also close to your current code! This is what you need:

 investinput :: IO (String, Int) investinput = do putStrLn "Enter Username : " username <- getLine putStrLn "Enter Invest Amount : " tempamount <- getLine let amount = read tempamount return (username, amount) 

Please note that I made only two changes (and deleted the empty line). Firstly, I changed the type of function, as I said above. Secondly, I changed show to read . The show function is of type Show a => a -> String : this is a function that takes everything that can be shown and creates a string representing it. You wanted read , which is of type Read a => String -> a : given by a string, it parses it and returns some read value.

The other thing you asked about returns a tuple (String, Int) instead of the IO (String, Int) action. 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 is an unclean action that depends on the real world. If we had such a function impossibleRunIO :: IO a -> a , then we would like it to be so that impossibleRunIO getLine == impossibleRunIO getLine , since the function must be clean. But this is useless, because we would like impossibleRunIO to really interact with the real world! Therefore, this pure function is not possible. Everything that is included in IO never leaves. This is what return does: it is a function, in this case 1 type return :: a -> IO a , which allows you to put pure values ​​in IO . For any x , return x is the action that always produces x at startup. This is why you should end your do block with return : username is the pure value you extracted from the action, and as such is only visible in the do block. You need to raise it in IO before the outside world can see it. The same goes for amount / tempamount .

And just for the sake of completeness: behind this there is some kind of comprehensive theory that links it. But this is not at all necessary to start Haskell programming. What I would recommend doing is structuring most of your code as pure functions that add, spindle, and cripple your data. Then build a thin (as thin as possible) IO front layer that interacts with the specified features. You will be surprised how little you need!

1: In fact, it has a more general type, but not relevant at the moment.

+8
source

Yes, you are almost there, but I think you want a 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, not show.

+4
source

The IO function that creates the tuple is of type IO (a, b) , in this case:

 investinput :: IO ([Char], Int) 

The signature IO () -> ([Char], Int) means that the function takes a parameter of type IO () and returns a tuple from it, which you don't like.

As a rule, there are no restrictions on the types that the IO function (or a function in another monad) can return, you can choose the types that you like.

+2
source

The answer to your question about returning (String, Int) rather than IO (String, Int) is simple: you cannot. When you end up in IO , you are stuck there. This is part of what it means when people say that Haskell is a β€œpure” language.

What you want to do is similar to what you are already doing here with getLine . The type of getLine is IO String . When you write username <- getLine , you actually take the String out of the IO String , but this is only possible because you are inside the do statement.

You can do the same with investinput as with getLine . Here is an example of how you could use investinput in your main function:

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

Since you mark liftM in the comment, here is the full working version that does the same thing using liftM and the liftM operator ( =<< ) instead of the 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 that expects a tuple."

+1
source

Source: https://habr.com/ru/post/1313142/


All Articles