Snap framework - repeated maybe tidying - haskell

I'm applying my (limited) Haskell knowledge to the Snap web framework and seeing what I can build. I'm trying to get a (possibly non-existent) parameter and parse it into an int. Obviously "Maybe" is what I'll be wanting.
In the code below AppHandler is defined as Handler App App (a monad with two levels of state I think, although I can't find anything in the tutorial now). The B8 is ByteString.Char8 and readInt returns Maybe(Int,ByteString)
The code below works, but presumably there should be a way to chain the maybe calls together (via MaybeT presumably, since I'm in a Monad already). The chaining makes particular sense because the next step will be to fetch a row from the database based on the parsed id, so of course that will return a "Maybe a" too. Clearly that's a pattern that's going to be very common.
-- Given a parameter name, return the Int it points to
-- or Nothing if there is some problem
maybeIntParam :: ByteString -> AppHandler (Maybe Int)
maybeIntParam pname = do
raw_param <- getParam pname
let mb_i_b = maybe (Nothing) B8.readInt raw_param
case mb_i_b of
Nothing -> return Nothing
Just (p,_) -> return $ Just p
I tried applying runMaybeT but with no real understanding of what types need changing frankly I was making random changes in the hope the error goes away. It didn't, although it changed and moved around from line to line.
I'm treating this as progress, since I'm now completely lost at a much higher level than I was when I started exploring Haskell...
Edit: walking through kosmikus' answer, hopefully I've understood it...
1 maybeIntParam :: ByteString -> AppHandler (Maybe Int)
2 maybeIntParam pname = do
3 raw_param <- getParam pname
4 return $ do
5 param <- raw_param
6 (p, _) <- B8.readInt param
7 return p
I think I was trying to inch towards this but kept trying to force getParam inside the same block as the other steps.
On line 3, the call to getParam is taking place in the AppHandler. We've got raw_param which is a Maybe ByteString. On line 5 we're in a nested do, so the binding(?) is taking place inside the Maybe monad and param will either be a ByteString or we get Nothing and the rest of the do-block will short-circuit*. Likewise on line 6, p is either an Int or we short-circuit.
All being well, on line 6, p contains an Int (say 42), and line 7 will return Just 42. Back at line 4 that becomes AppHandler (Just 42). Don't need to care what an AppHandler is at the moment - the types are all happy.
Here are some variations that also type-check and may prove of use to those trying to think this through.
maybeIntParam1 :: ByteString -> AppHandler (Maybe Int)
maybeIntParam1 pname = do
raw_param <- getParam pname
let mb_int_param = do
param <- raw_param
(p, _) <- B8.readInt param
return p
return mb_int_param
maybeIntParam2 :: ByteString -> AppHandler (Maybe Int)
maybeIntParam2 pname = do
return $ return 27
maybeIntParam3 :: ByteString -> AppHandler (Maybe Int)
maybeIntParam3 pname = do
return (Just 27)
The non-do variation actually seems simpler in this case. the only bit that needs thinking about is the <$> which if I'm reading right is just fmap and applies the fst to Maybe (Int,ByteString) so we can get Maybe Int.
* If I understand correctly, each subsequent line must be visited but just returns Nothing, so not actually a goto-style shortcut.
Edit2: see kosmikus' comment below - laziness + right-nesting means we don't need to evaluate each line.

You can just locally use the Maybe monad here:
maybeIntParam :: ByteString -> AppHandler (Maybe Int)
maybeIntParam pname = do
raw_param <- getParam pname
return $ do
param <- raw_param
(p, _) <- B8.readInt param
return p
Or if you prefer, you can write the Maybe-computations as a one-liner:
maybeIntParam :: ByteString -> AppHandler (Maybe Int)
maybeIntParam pname = do
raw_param <- getParam pname
return $ fst <$> (raw_param >>= B8.readInt)
Some of the types involved:
raw_param :: Maybe ByteString
B8.readInt :: ByteString -> Maybe (Int, ByteString)
raw_param >>= B8.readInt :: Maybe (Int, ByteString)
fst :: (Int, ByteString) -> Int
fst <$> (raw_param >>= B8.readInt) :: Maybe Int

Related

Refactoring Haskell when adding IO

I have a concern regarding how far the introduction of IO trickles through a program. Say a function deep within my program is altered to include some IO; how do I isolate this change to not have to also change every function in the path to IO as well?
For instance, in a simplified example:
a :: String -> String
a s = (b s) ++ "!"
b :: String -> String
b s = '!':(fetch s)
fetch :: String -> String
fetch s = reverse s
main = putStrLn $ a "hello"
(fetch here could more realistically be reading a value from a static Map to give as its result)
But say if due to some business logic change, I needed to lookup the value returned by fetch in some database (which I can exemplify here with a call to getLine):
fetch :: String -> IO String
fetch s = do
x <- getLine
return $ s ++ x
So my question is, how to prevent having to rewrite every function call in this chain?
a :: String -> IO String
a s = fmap (\x -> x ++ "!") (b s)
b :: String -> IO String
b s = fmap (\x -> '!':x) (fetch s)
fetch :: String -> IO String
fetch s = do
x <- getLine
return $ s ++ x
main = a "hello" >>= putStrLn
I can see that refactoring this would be much simpler if the functions themselves did not depend on each other. That is fine for a simple example:
a :: String -> String
a s = s ++ "!"
b :: String -> String
b s = '!':s
fetch :: String -> IO String
fetch s = do
x <- getLine
return $ s ++ x
doit :: String -> IO String
doit s = fmap (a . b) (fetch s)
main = doit "hello" >>= putStrLn
but I don't know if that is necessarily practical in more complicated programs.
The only way I've found thus far to really isolate an IO addition like this is to use unsafePerformIO, but, by its very name, I don't want to do that if I can help it. Is there some other way to isolate this change? If the refactoring is substantial, I would start to feel inclined to avoid having to do it (especially under deadlines, etc).
Thanks for any advice!
Here are a few methods I use.
Reduce dependencies on effects by inverting control. (One of the methods you described in your question.) That is, execute the effects outside and pass the results (or functions with those results partially applied) into pure code. Instead of having main → a → b → fetch, have main → fetch and then main → a → b:
a :: String -> String
a f = b f ++ "!"
b :: String -> String
b f = '!' : f
fetch :: String -> IO String
fetch s = do
x <- getLine
return $ s ++ x
main = do
f <- fetch "hello"
putStrLn $ a f
For more complex cases of this, where you need to thread an argument to do this sort of “dependency injection” through many levels, Reader/ReaderT lets you abstract over the boilerplate.
Write pure code that you expect might need effects in monadic style from the start. (Polymorphic over the choice of monad.) Then if you do eventually need effects in that code, you don’t need to change the implementation, only the signature.
a :: (Monad m) => String -> m String
a s = (++ "!") <$> b s
b :: (Monad m) => String -> m String
b s = ('!' :) <$> fetch s
fetch :: (Monad m) => String -> m String
fetch s = pure (reverse s)
Since this code works for any m with a Monad instance (or in fact just Applicative), you can run it directly in IO, or purely with the “dummy” monad Identity:
main = putStrLn =<< a "hello"
main = putStrLn $ runIdentity $ a "hello"
Then as you need more effects, you can use “mtl style” (as #dfeuer’s answer describes) to enable effects on an as-needed basis, or if you’re using the same monad stack everywhere, just replace m with that concrete type, e.g.:
newtype Fetch a = Fetch { unFetch :: IO a }
deriving (Applicative, Functor, Monad, MonadIO)
a :: String -> Fetch String
a s = pure (b s ++ "!")
b :: String -> Fetch String
b s = ('!' :) <$> fetch s
fetch :: String -> Fetch String
fetch s = do
x <- liftIO getLine
return $ s ++ x
main = putStrLn =<< unFetch (a "hello")
The advantage of mtl style is that you can have multiple different implementations of your effects. That makes things like testing & mocking easy, since you can reuse the logic but run it with different “handlers” for production & testing. In fact, you can get even more flexibility (at the cost of some runtime performance) using an algebraic effects library such as freer-effects, which not only lets the caller change how each effect is handled, but also the order in which they’re handled.
Roll up your sleeves and do the refactoring. The compiler will tell you everywhere that needs to be updated anyway. After enough times doing this, you’ll naturally end up recognising when you’re writing code that will require this refactoring later, so you’ll consider effects from the beginning and not run into the problem.
You’re quite right to doubt unsafePerformIO! It’s not just unsafe because it breaks referential transparency, it’s unsafe because it can break type, memory, and concurrency safety as well—you can use it to coerce any type to any other, cause a segfault, or cause deadlocks and concurrency errors that would ordinarily be impossible. You’re telling the compiler that some code is pure, so it’s going to assume it can do all the transformations it does with pure code—such as duplicating, reordering, or even dropping it, which may completely change the correctness and performance of your code.
The main legitimate use cases for unsafePerformIO are things like using the FFI to wrap foreign code (that you know is pure), or doing GHC-specific performance hacks; stay away from it otherwise, since it’s not meant as an “escape hatch” for ordinary code.
First off, the refactoring doesn't tend to be as bad as you might imagine. Once you make the first change, the type checker will point you to the next few, and so on. But suppose you have a reason to suspect from the start that you might need some extra capability to make a function go. A common way to do this (called mtl-style, after the monad transformer library) is to express your needs in a constraint.
class Monad m => MonadFetch m where
fetch :: String -> m String
a :: MonadFetch m => String -> m String
a s = fmap (\x -> x ++ "!") (b s)
b :: MonadFetch m => String -> m String
b s = fmap (\x -> '!':x) (fetch s)
instance MonadFetch IO where
-- fetch :: String -> IO String
fetch s = do
x <- getLine
return $ s ++ x
instance MonadFetch Identity where
-- fetch :: String -> Identity String
fetch = Identity . reverse
You're no longer tied to a particular monad: you just need one that can fetch. Code operating on an arbitrary MonadFetch instance is pure, except that it can fetch.

Unwrapping a from IO (a)

I've been learning Haskell in the last 2 weeks and decided to try challenges at places such as HackerRank. This has required learning IO. I have read many answers on StackExchange and the general gist is you don't unwrap IO a, you just manipulate that data inside the IO function. That being the case what is the point of all the pure functions, if I'm not allowed to send data from main out to them? Here is some code that reads how many test cases, then for each test case reads N ordered pairs.
main = do
test <- getLine
replicateM (read test) doTest
doTest = do
query<-getLine
rs<-replicateM (read query) readPair
return rs -- just here to make the file compile
readPair :: IO (Int, Int)
readPair = do
input <- getLine
let a = words input in return (read (a!!0) :: Int, read (a!!1) ::Int)
At this point I have a IO [(Int, Int)] inside of rs. I would like to send that data to this function:
validFunction :: [(Int,Int)]->Bool
validFuntion [] = True
validFunction (x:[]) = True
validFunction (x:xs) = (not $ elem (snd x) (fmap snd xs)) && validFunction xs
But I can't seem to figure out how to do that. Any help or suggestions about how to call this function with the data I've read from the user would be appreciated. Or if I'm going about it from the wrong angle, and pointers on what I should be doing would also work.
Edit: From reading lots of other questions on here I now have the general idea that once you're in IO you're stuck there. But what I can't seem to find is the syntax to call a pure function with IO data and get back IO data. I've tried some of the following :
fmap validFunction [rs] :: IO Bool -- tried it with just rs without [] as well
mapM validFunction [rs] :: IO Bool
validFunction rs :: IO Bool
I was able to get this to work:
putStrLn . f . validFunction $ rs
though I'm still not clear on why this lets you pass the IO [(Int, Int)] to validFunction.
First of all, if you use x <- act in do, you essentially have a value. Unless you did something very suspicious, x isn't a IO something, but a something: So it's perfectly fine to use
foo :: Int -> Char
foo = …
bar :: IO Int
bar = …
fooDo :: IO Char
fooDo = do
number <- bar
return (foo number) -- apply foo directly on number
However, IO is an instance of Functor, so we can use fmap to lift foo:
liftedFoo :: IO Int -> IO Char
liftedFoo = fmap foo
So we could have written fooDo like this:
fooDo = fmap foo readLn
Although it's name is now misleading, it still does the same as before. But let's leave this naming voodoo aside, how would you tackle this? Well, your doTest has the correct type:
doTest :: IO [(Int, Int)]
doTest = do
query <- getLine
rs <- replicateM (read query) readPair
return rs
So all that's missing is calling validFunction. We can do that like in fooDo:
doTest :: IO Bool
doTest = do
query <- getLine
rs <- replicateM (read query) readPair
return (validFunction rs)
-- ^^^^^^^^^^^^^^^^^^
-- no IO inside here
-- ^^^^^^
-- back
-- to IO
Or we can fmap over another IO value, like replicateM (read query) readPair:
doTest :: IO Bool
doTest = do
query <- getLine
fmap validFunction (replicateM (read query) readPair)
The latter is harder to read, though. But you write your fooDo doTest as you want to do.

Reading from file list of chars or list of ints

I have a question. There is any solution for reading from file list of tuples ? Depends on content ?
I know that if i need to read integers i do something like that:
toTuple :: [String] -> [(Int,Int)]
toTuple = map (\y -> read y ::(Int,Int))
But in file i can have tuples this kind (int,int) or (char, int). Is any way to do this nice ?
I was trying to do this at first in finding sign " ' " . If it was, then reading chars, but it doesn't work for some reason.
[Edit]
To function to tuple, i give strings with tuples, before that i splits lines by space sign.
INPUT EXAMPLE:
Case 1 : ["(1,2)", "(1,3)" ,"(3,4)" ,"(1,4)"]
Case 2 : ["('a',2)", "('b',3)", "('g',8)", "('h',2)", "('r',4)"]
Just try both and choose the successful:
import Text.Read
import Control.Applicative
choose :: Maybe a -> Maybe b -> Maybe (Either a b)
choose x y = fmap Left x <|> fmap Right y
readListMaybe :: Read a => [String] -> Maybe [a]
readListMaybe = mapM readMaybe
toTuple :: [String] -> Maybe (Either [(Int, Int)] [(Char, Int)])
toTuple ss = readListMaybe ss `choose` readListMaybe ss
main = do
-- Just (Left [(1,2),(1,3),(3,4),(1,4)])
print $ toTuple ["(1,2)", "(1,3)" ,"(3,4)" ,"(1,4)"]
-- Just (Right [('a',2),('b',3),('g',8),('h',2),('r',4)])
print $ toTuple ["('a',2)", "('b',3)", "('g',8)", "('h',2)", "('r',4)"]
Here is a far more efficient (and unsafe) version:
readListWithMaybe :: Read a => String -> [String] -> Maybe [a]
readListWithMaybe s ss = fmap (: map read ss) (readMaybe s)
toTuple :: [String] -> Either [(Int, Int)] [(Char, Int)]
toTuple [] = Left []
toTuple (s:ss) = fromJust $ readListWithMaybe s ss `choose` readListWithMaybe s ss
In the first definition of toTuple
toTuple :: [String] -> Maybe (Either [(Int, Int)] [(Char, Int)])
toTuple ss = readListMaybe ss `choose` readListMaybe ss
readListMaybe is too strict:
readListMaybe :: Read a => [String] -> Maybe [a]
readListMaybe = mapM readMaybe
mapM is defined in terms of sequence which is defined in terms of (>>=) which is strict for the Maybe monad. And also the reference to ss is keeped for too long. The second version doesn't have these problems.
As I said it may be a good idea to consider using a parsing library, if the task at hand gets a bit more complicated.
First of all you have the benefit of getting error messages and if you decide to switch to a self declared data Type it is still easily applicable (with slight modifications of course).
Also switching from ByteString to Text (which are both preferable to working with String anyways) is just a matter of (un)commenting 4 lines
Here is some example if you have not had the pleasure to work with it.
I'll explain it some time later today - for I have to leave now.
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Attoparsec.ByteString.Char8
import Data.ByteString.Char8 as X
-- import Data.Attoparsec.Text
-- import Data.Text as X
main :: IO ()
main = do print <$> toTuples $ X.unlines ["(1,2)","(1,3)","(3,4)","(1,4)"]
print <$> toTuples $ X.unlines ["('a',2)","('h',2)","('r',4)"]
print <$> toTuples $ X.unlines ["('a',2)","(1,3)","(1,4)"] --works
print <$> toTuples $ "('a',2)" -- yields Right [Right ('a',2)]!!
print <$> toTuples $ "(\"a\",2)" -- yields Right []!!
toTuples = parseOnly (myparser `sepBy` skipSpace :: Parser [Either (Int,Int) (Char,Int)])
where myparser :: Parser (Either (Int,Int) (Char,Int))
myparser = eitherP (tupleP decimal decimal)
(tupleP charP decimal)
charP = do char '\''
c <- notChar '\''
char '\''
return c
tupleP :: Parser a -> Parser b -> Parser (a, b)
tupleP a b = do char '('
a' <- a
skipSpace
char ','
skipSpace
b' <- b
char ')'
return (a',b')
Edit: Explanation
Parser is a monad, so it comes with do-notation which enables us to write the tupleP function in this very convenient form. Same goes for charP - we describe what to parse in the primitives given by the attoparsec library
and it reads something like
first expect a quote
then something that is not allowed to be a quote
and another quote
return the not quote thingy
if you can write down the parser informally you're most likely halfway through writing the haskell code, the only thing left to do is find the primitives in the library or write some auxilary function like tupleP.
A nice thing is that Parsers (being monads) compose nicely so we get our desired parser eitherP (tupleP ..) (tupleP ..).
The only magic that happens in the print <$>.. lines is that Either is a functor and every function using <$> or fmap uses the Right side of the Eithers.
Last thing to note is sepBy returns a list - so in the case where the parsing fails we still get an empty list as a result, if you want to see the failing use sepBy1 instead!

How to properly use the readMaybe function in IO

I started with programming in Haskell about 4 month ago and now I came to the point where I have to deal with the IO system of Haskell.
I already did a lot of IO actions and haven't faced any problems I couldn't solve by myself, but this time I googled for almost two hours for no avail to get some information about the function readMaybe. So I have the following problem set to solve and I already tried a lot of different approaches to solve it but all the time I get the same failure message from my compiler:
No instance for (Read a0) arising from a use of `readMaybe'
The type variable `a0' is ambiguous
I understand what the compiler does want to tell me but I have no idea how to solve this problem. I already tried to add a class constraint, but without success.
So here is my very small and simple program that is just counting how many valid numbers the user has entered. The program is meant to terminate when the user enters an empty line.
This is just a auxiliary function I want to use for my project later on.
countNumbers :: IO Int
countNumbers = do
x <- count 0
return x where
count :: Int -> IO Int
count n = do
line <- getLine
case line of
"" -> do
return n
_ -> case readMaybe line of
Just _ -> do
x <- count (n+1)
return x
Nothing -> do
x <- count n
return x
Unfortunately I couldn't find out a lot of informations about the function readMaybe. The only thing I could find was in the Haskell library Text.Read:
readMaybe :: Read a => String -> Maybe aSource
Parse a string using the Read instance. Succeeds if there is exactly one valid result.
The very weird thing for me is that I have already written such a function that uses the readMaybe function and it worked perfectly ...
This program is just asking the user for a number and keeps asking as long as the user enters a valid number
getLineInt :: IO Int
getLineInt = do
putStrLn "Please enter your guess"
line <- getLine
case readMaybe line of
Just x -> do
return x
Nothing -> do
putStrLn "Invalid number entered"
x <- getLineInt
return x
So far as I can see there are no differences between the usage of the function readMaybe in the two programs and therefore it works in the one but not in the other :)
I would be really thankful for any hints from you!!
This has nothing to do with IO, so maybe you don't understand what the compiler is trying to tell you. There is a type variable a in readMaybe's signature; a has to have a Read instance, but other than that it can be anything. The compiler is telling you that it doesn't have any way to determine what you want a to be.
In getLineInt you don't have this problem, because you are returning the result of readMaybe and the type signature says it should be Int. In countNumbers, you're not using the result of readMaybe, so there's nothing that can be used to determine the correct type. You can fix this by adding an explicit type signature (I picked Int since you're apparently counting numbers):
_ -> case readMaybe line :: Maybe Int of
Finally a word about do notation: it's just syntactic sugar, you don't have to use it all the time. Instead of do return x you can simply write return x, and instead of
x <- getLineInt
return x
you can simply do
getLineInt
That makes things more readable:
getLineInt :: IO Int
getLineInt = do
putStrLn "Please enter your guess"
line <- getLine
case readMaybe line of
Just x -> return x
Nothing -> putStrLn "Invalid number entered" >> getLineInt
Why does this happen?
In your second function, it is clear that readMaybe line is used as String -> Maybe Int, since type inference notices that you use return x and therefore x must be an Int.
In your first function, you don't use the Maybe's value at all, you just want to check whether the read succeeded. However, since you didn't specify the type (neither explicit nor implicit with type inference), the type variable is ambiguous:
_ -> case readMaybe line of
There's an easy fix: annotate the type:
_ -> case readMaybe line :: Maybe Int of
By the way, this is exactly the same behaviour you encounter when you use read in ghci without any type context:
> read "1234"
<interactive>:10:1:
No instance for (Read a0) arising from a use of `read'
The type variable `a0' is ambiguous
As soon as you make the type clear everything is fine:
> read "1234" :: Int
1234
Making things clear
Now that we've seen why the error happens, lets make this program much simpler. First of all, we're going to use a custom readMaybe:
readMaybeInt :: String -> Maybe Int
readMaybeInt = readMaybe
Now how does one count numbers? Numbers are those words, where readMaybeInt doesn't return Nothing:
countNumbers :: String -> Int
countNumbers = length . filter isJust . map readMaybeInt . words
How does one now calculate the numbers in the standard input? We simply take input until one line is completely empty, map countNumbers on all those lines and then sum:
lineNumberCount :: IO Int
lineNumberCount =
getContents >>= return . sum . map countNumbers . takeWhile (/= "") . lines
If you're not used to the bind methods, that's basically
lineNumberCount :: IO Int
lineNumberCount = do
input <- getContents
return . sum . map countNumbers . takeWhile (/= "") . lines $ input
All in all we get the following terse solution:
import Control.Monad (liftM)
import Data.Maybe (isJust)
import Text.Read (readMaybe)
readMaybeInt :: String -> Maybe Int
readMaybeInt = readMaybe
countNumbers :: String -> Int
countNumbers = length . filter isJust . map readMaybeInt . words
lineNumberCount :: IO Int
lineNumberCount =
getContents >>= return . sum . map countNumbers . takeWhile (/= "") . lines
Now there's only one function working in the IO monad, and all functions are basically applications of standard functions. Note that getContents will close the handle to the standard input. If you want to use you're better of using something like
input :: String -> IO [String]
input delim = do
ln <- getLine
if ln == delim then return []
else input delim >>= return . (ln:)
which will extract lines until a line matching delim has been found. Note that you need to change lineNumberCount in this case:
lineNumberCount :: IO Int
lineNumberCount =
input "" >>= return . sum . map countNumbers

Haskell: Function using do notation and returning i.e. Integer value

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.
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.
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

Resources