Ask user for list input in Haskell - list

I found this code online, but it's not running.
main = do
xs <- getLine []
print xs
So how do I ask the user for list input in Haskell? I am new to Haskell, please explain when you answer. Thanks.

You do it e.g. like this:
main :: IO ()
main = do
xs <- getLine
let { ints :: [Int]
; ints = read xs
}
print $ take 2 ints
and you must type in the input in a valid list syntax, e.g.
[1,2,3]
Do take note, each line in a do-block must start at the same indentation level (unless explicit separators { ; } are used).

getLine is an IO action that produces a string, nothing else. You need to process that string once you receive it. As an example, here's an IO action that will parse an appropriate input into a list of Int values.
getIntList :: IO [Int]
getIntList = fmap read getLine
main = do
ints <- getIntList
print ints
There is a Read instance for lists, so the following works:
> read "[1,2,3]" :: [Int]
[1,2,3]
getIntList uses the Functor instance for IO to apply read to the string that getLine will produce; read's concrete type will be inferred from the type given to getIntList: since getIntList :: IO [Int], then fmap read :: IO String -> IO [Int], and so read :: String -> [Int] will be used.

Related

Make a record from a list of Text values

Starting from a list of Text, I'd like to convert this into a Record where each field corresponds to a value in the list.
import Data.Text
data MyRecord = MyRecord {
rOne :: Text,
rTwo :: Text,
rThree :: Int
}
txt :: [Text]
txt = ["foo", "bar", "12"]
I'm not sure how to (cleanly) proceed from the list of Text up to the record. It is assumed that this list will always be of the same size (here, 3) and there will always be an Int-like value in third position.
The minimal naive working version I've got to is so desperately hacky that I can't resign myself to committing this into the codebase:
-- assuming RecordWildCards
readText :: [Text] -> MyRecord
readText l =
let rOne = l !! 0
rTwo = l !! 1
rThree :: Int = read $ l !! 2
in MyRecord {..}
What would be a more Haskell-friendly way of doing this?
Note: what I'm actually after is parsing a comma-separated file into a list of MyRecord, without a csv library. The rows will not contain commas inside a value, so splitting on commas can be considered safe here (e.g. values = T.splitOn "," <$> lines).
If you are absolutely sure the list is three elements long, you could use
readText :: [Text] -> MyRecord
readText [rOne, rTwo, rThreeText] = MyRecord {..}
where rThree :: Int = read rThreeText
Still, you might wish to make the pattern matching exhaustive, just in case:
readText :: [Text] -> MyRecord
readText [rOne, rTwo, rThreeText] = MyRecord {..}
where rThree :: Int = read rThreeText
readText _ = error "readText: list length /= 3"

How to add to a List from IO in Haskell.

I am trying to write a simple function which reads in one line at a time (which I know will be integers) and then stores them in to a list. For the life of me however, it seems like the list comes out to empty.
import System.IO
import Control.Monad
type Int2 = [Int]
valueTab = [] :: [Int]
app [ ] list = list
app (h:t) list = h:(app t list)
main :: IO ()
main = do
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
-- Auto-generated code below aims at helping you parse
-- the standard input according to the problem statement.
input_line <- getLine
let n = read input_line :: Int
let value = [] :: [Int]
replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int
hPutStrLn stderr (show input_line)
hPutStrLn stderr (show valueTab)
return $ app valueTab [pi]
-- hPutStrLn stderr "Debug messages..."
-- Write answer to stdout
--putStrLn input_line
return ()
So when I run this with
8
6
4
3 all on their own lines,
It prints 6, [], 4, [], 3 [].
Is this a problem with my printing, with my list declaration, with the way that I store them or? I have both value and valueTab to check whether it was a scope problem.
Note: The code is boilerplate code on a coding website that tests it on it's platform. Assume replicateM is just a loop that runs through the code x number of times.
It looks like you're doing a problem on codinggame.com. Other coding sites seem to do a better job with their Haskell templates, particularly for beginner exercises -- the template usually takes care of all the input and output, and you just need to supply the missing pure function. In contrast, codinggame.com's Haskell templates seem to assume the beginner has a pretty firm grasp of the IO monad, and leave out a lot of essential details (e.g., the fact that the result of the replicateM action should actually be bound to a variable) which you're expected to fill in.
The replicateM call in the original template probably looked something like:
replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int -- maybe this line was there, maybe not
return ()
This expression creates a composite IO action that, when executed, will repeat the following subaction n times:
read a line, binding the string read to input_line
prepare to convert that line to an integer value pi (though this is never done because pi isn't used)
regardless of the string read, return "unit" (the value ()) as the result of the subaction
The value of the composite action is then a list of the values returned by the subactions. Since those were all units, the final value of the composite action is a list [(),(),()...()], with one () for each line read. However, because the value of this composite action is never bound to a variable (i.e., because there's no result <- ... before the replicateM expression), this list is thrown away.
So, this template provides a needlessly verbose method of reading n lines of input and doing nothing with them.
Fortunately, all you need to do to make this template do something useful is to have the subaction return a value that's useful (e.g., the integer pi) which will cause the composite action to return a list of the integers read, and then make sure you bind the resulting list to a variable using the <- notation.
In other words, you want to write:
main = do
...
pis <- replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int
return pi
hPutStrLn stderr (show pis)
You won't need the helper function app, and you won't need to pre-declare a list valueTab to contain the result. The result is automatically produced by replicateM, and you just need to name it so you can use it.
The complete working program will look like:
import System.IO
import Control.Monad
type Int2 = [Int]
main :: IO ()
main = do
hSetBuffering stdout NoBuffering -- DO NOT REMOVE
-- Auto-generated code below aims at helping you parse
-- the standard input according to the problem statement.
input_line <- getLine
let n = read input_line :: Int
let value = [] :: [Int]
pis <- replicateM n $ do
input_line <- getLine
let pi = read input_line :: Int
return pi
hPutStrLn stderr (show pis)
-- hPutStrLn stderr "Debug messages..."
-- Write answer to stdout
return ()

Print elements of a list in the same line

I'm new at haskell and I'm trying to print the elements of a list in a same line . For example:
[1,2,3,4] = 1234
If elements are Strings I can print it with mapM_ putStr ["1","2","3","\n"]
but they aren't.. Someone knows a solution to make a function and print that?
I try dignum xs = [ mapM_ putStr x | x <- xs ] too buts don't work ..
You can use show :: Show a => a -> String to convert an element (here an integer), to its textual representation as a String.
Furthermore we can use concat :: [[a]] -> [a] to convert a list of lists of elements to a list of elements (by concatenating these lists together). In the context of a String, we can thus use concat :: [String] -> String to join the numbers together.
So we can then use:
printConcat :: Show a => [a] -> IO ()
printConcat = putStrLn . concat . map show
This then generates:
Prelude> printConcat [1,2,3,4]
1234
Note that the printConcat function is not limited to numbers (integers), it can take any type of objects that are a type instance of the Show class.

Haskell: how to print each element of list separated with comma

I am trying to print list with comma.
I have list like ["1","2","3"] and I want to print 1,2,3
How can I do that?
I tried:
printList xs = mapM_ (\(a) -> do
putStr a
putStr (",")) xs
But I dont know how to remove the last comma.
You can use intercalate. It'll insert the comma between each element of the list and concatenate the resulting list of strings to turn it into a single string.
import Data.List
toCommaSeparatedString :: [String] -> String
toCommaSeparatedString = intercalate ","
ghci> toCommaSeparatedString ["1","2","3"]
"1,2,3"
This is a bit of an XY problem: as Benjamin Hodgson shows, you’re better off turning your list into a string, and then printing that – you want as much of your logic outside of the IO monad as possible.
But of course, even if your question is somewhat in the wrong direction from the start, it has an answer! Which is that, for example, you could write this:
printList :: [String] -> IO ()
printList [] = return ()
printList [x] = putStr x
printList (x:xs) = do
putStr x
putStr ","
printList xs
Benjamin’s answer is better. But this one might elucidate IO monad code and do-notation a bit more.

Haskell IO - file lines to list

I am writing a haskell program and am having trouble with IO types. It seems something is wrong with my readLines function and the type it is supposed to return. I want it to return a list where each element is a line from the file that is opened with the openDict function.
Here is my code.
main :: IO ()
main = do
fileHandle <- openDict
readLines fileHandle
putStr "End of program\n"
readLines :: Handle -> [IO String]
readLines fileHandle
| isEOF <- hIsEOF fileHandle = []
| otherwise = [hGetLine fileHandle] ++ readLines fileHandle
openDict :: IO Handle
openDict = do
putStr "Enter file path: "
filePath <- getLine
openFile filePath ReadMode
Here is the error:
Couldn't match type `[]' with `IO'
Expected type: IO (IO String)
Actual type: [IO String]
In the return type of a call of `readLines'
In a stmt of a 'do' block: readLines fileHandle
In the expression:
do { fileHandle <- openDict;
readLines fileHandle;
putStr "End of program" }
So basically, how can I store these strings from IO in a list?
Your readLines function has several problems.
First, in this case you do not want to return a [IO String]. This would be a list of IO actions such as [print 1 >> return "a", print 2 >> return "b"] which is not what you want. You need a single IO action returning a list of Strings, so you need to use IO [String].
Second, the pattern guard isEof <- hIsEOF fileHandle is checking whether the value returned by hIsEOF (which is an IO action) is of the form isEOF, where isEOF is a variable being defined right now. Of course anything is of the form var, since a variable can be anything. So the test is always true, and not meaningful: it does not even run the IO action, for instance.
Third, the way you combine the list constructors and IO actions follows the current wrong type,
so it is quite confusing.
A quite verbose way to achieve this would be instead:
readLines :: Handle -> IO [String]
readLines fileHandle = do
eof <- hIsEOF fileHandle
if eof then return []
else do line <- hGetLine fileHandle
rest <- readLines fileHandle
return (line:rest)
The last lines can be shortened a bit using Applicative syntax:
import Control.Applicative
readLines2 :: Handle -> IO [String]
readLines2 fileHandle = do
eof <- hIsEOF fileHandle
if eof then return []
else (:) <$> hGetLine fileHandle <*> readLines2 fileHandle
A short version can be instead obtained by reading everything in the file and splitting into lists after that using the lines library function.
import Control.Applicative
readLines3 :: Handle -> IO [String]
readLines3 fileHandle = lines <$> hGetContents fileHandle
Here is the readLines that you likely really want.
readLines :: Handle -> IO [String]
readLines h = do
isEOF <- hIsEOF h -- need to access isEOF directly, without monad, see comment about the bind
case isEOF of
True -> return [] -- the return adds the IO monad to the [String] type
False -> do
line <- hGetLine h -- need to access line separately without monad
lines <- readLines h -- recurse down
return $ line ++ lines -- return adds back the IO monad