Question

I want to write a function that read some data using getLine and return i.e. a tuple (Integer, Integer) but using do-notation. Something like this (of course it doesn't work):

fun :: (Integer, Integer)
fun = do
    a <- read (getLine::Integer)
    b <- read (getLine::Integer)
    return (a, b)

Do I have to write my own monad for this? Is there any solution to not writing a new monad?


EDIT

So I can write main function that use fun, I think it's the only solution:

main :: IO ()
main = do
    tuple <- fun
    putStrLn (show tuple)

fun :: IO (Integer, Integer)
fun = do
    a1 <- getLine
    b1 <- getLine
    let a = read (a1)
        b = read (b1)
    return (a, b)

And above code works.

Was it helpful?

Solution

You type of function should be

fun :: IO (Integer, Integer)

as mentioned by @kaan you should not try to get a mondic value (with side effects) out of the monad as that will break referential transparency. Running fun should always return same value no matter how many times it is run and if we use your type this will not happen. However if the type is IO (Integer, Integer) then it returns the same action every time you use that function and running this action actually perform the side effect of reading the values from the console.

Coming back to using you function. You can do that inside another IO monad like

main = do
  (a,b) <- fun
  print a
  print b

Although there are ways of getting things out of IO using unsafe functions but that is not recommended until you know exactly what you are doing.

OTHER TIPS

As mentioned, you will need to give fun the type IO (Integer, Integer) instead of (Integer, Integer). However, once you have resigned yourself to this fate, there are many ways to skin this cat. Here are a handful of ways to get your imagination going.

fun = do
    a <- getLine
    b <- getLine
    return (read a, read b)

-- import Control.Applicative for (<$>)
-- can also spell (<$>) as fmap, liftA, liftM, and others
fun = do
    a <- read <$> getLine
    b <- read <$> getLine
    return (a, b)

fun = do
    a <- readLn
    b <- readLn
    return (a, b)

fun = liftM2 (,) readLn readLn

-- different type!
-- use in main like this:
-- main = do
--        [a, b] <- fun
--        foo
-- import Control.Monad for replicateM
fun :: IO [Integer]
fun = replicateM 2 readLn
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top