Haskell -- Main.hs:18:5: parse error on input ‘putStrLn’ - haskell

module Main where
reportResults :: [String] -> [Int] -> IO ()
reportResults fileNames exitCodes = do
putStrLn "All Files"
putStrLn "---------"
putStr.unlines $ map (" " ++) fileNames
putStrLn ""
let problems = filter (\p -> fst p /= 0) $ zip exitCodes fileNames
putStrLn "Problematic Files"
putStrLn "-----------------"
mapM_ (putStrLn . showProblem) problems
where showProblem :: (Int, String) -> String
showProblem (c, f) = " " ++ show c ++ " - " ++ f
putStrLn "Done!" -- "Parse error on input...". OK if this line is removed.
main :: IO ()
main = do
let fileNames = ["A", "B", "C", "D"]
let exitCodes = [0, 1, 2, 3]
reportResults fileNames exitCodes
The code works fine if I comment out, or remove, the offending line (line 18), but I would really like to retain it and also understand what I'm doing wrong. After trying many permutations and searching heaps, I still can't crack it. Any help would be greatly appreciated.

You can define the showProblem function in a let clause, so:
reportResults :: [String] -> [Int] -> IO ()
reportResults fileNames exitCodes = do
putStrLn "All Files"
putStrLn "---------"
putStr.unlines $ map (" " ++) fileNames
putStrLn ""
let problems = filter (\p -> fst p /= 0) $ zip exitCodes fileNames
putStrLn "Problematic Files"
putStrLn "-----------------"
let showProblem (c, f) = " " ++ show c ++ " - " ++ f
mapM_ (putStrLn . showProblem) problems
putStrLn "Done!" -- "Parse error on input...". OK if this line is removed.

Related

How to fix writing a list content in a file

I have a function which contains a list. I want just to write that list content in a file from main after user input.
putTodo :: (Int, String) -> IO ()
putTodo (n, todo) = putStrLn (show n ++ ": " ++ todo)
prompt :: [String] -> IO ()
prompt todos = do
putStrLn "The list contains:"
mapM_ putTodo (zip [0..] todos)
putStrLn " "
command <- getLine
getCommand command todos
What I tried:
main = do
outh <- openFile "agenda.txt" WriteMode;
hPutStrLn outh prompt[]
-- hPutStrLn outh (show prompt[])
-- hPrint (show prompt[])
hClose outh;
Thank you.
Your code contains a couple of errors / problems:
prompt[] isn't valid (in main) - this should be prompt
hPutStrLn expects a String as its second argument, but you provide IO()
getCommand is not defined
What you need is:
a list of todos (possibly returned by a function)
a function that converts this list of todos to a string
hPutStrLn to print this string to the output file
Here's a simple version with a hard-coded list of todos (my Haskell isn't very advanced, so this could probably be done in a much more elegant way):
import System.IO
type Todo = (Int, String)
todoToString :: Todo -> String
todoToString (idx, name) = (show idx) ++ " : " ++ name
todosToString :: [Todo] -> String
todosToString todos = foldl (\acc t -> acc ++ "\n" ++ (todoToString t)) "" todos
allTodos :: [Todo]
allTodos = [(1, "Buy milk"), (2, "Program Haskell")]
main = do
outh <- openFile "agenda.txt" WriteMode;
hPutStrLn outh (todosToString allTodos);
hClose outh;

Haskell --- error using SplitOn ","

getLines = liftM lines . readFile
main = do
argv <- getArgs
name <- getProgName
if not (null argv)
then do
let file = head argv
list <- getLines file
let olist = mergesort (<=) list
let splitter = splitOn "," olist
loop olist
else hPutStr stderr $ "usage: " ++ name ++ " filename"
loop a = do
line <- getLine
case line of
"help" -> putStrLn "print - prints list in alphabetical order\n\
\quit - exits program"
"print" -> do putStrLn "[print]"
mapM_ putStrLn a
putStr "\n"
"quit" -> do putStrLn "[quit]"
exitSuccess
_ -> putStrLn "invalid command"
loop a
I'm getting this error:
Couldn't match type '[Char]' with `Char'
Expected type: [Char]
Actual type: [String]
any tips?
You need to use single quotes for char constants.
See this
let splitter = splitOn ',' olist

Can I be sure of order of IO actions in this example?

At the moment, I have this code in and around main:
import Control.Monad
import Control.Applicative
binSearch :: Ord a => [a] -> a -> Maybe Int
main = do
xs <- lines <$> readFile "Cars1.txt"
x <- getLine <* putStr "Registration: " -- Right?
putStrLn $ case binSearch xs x of
Just n -> "Found at position " ++ show n
Nothing -> "Not found"
My hope is for “Registration: ” to be printed, then for the program to wait for the input to x. Does what I've written imply that that will be the case? Do I need the <*, or will putting the putStr expression on the line above make things work as well?
PS: I know I have to convert binSearch to work with arrays rather than lists (otherwise it's probably not worth doing a binary search), but that's a problem for another day.
The line
x <- getLine <* putStr "Registration: "
orders the IO actions left-to-right: first a line is taken as input, then the message is printed, and finally variable x is bound to the result of getLine.
Do I need the <*, or will putting the putStr expression on the line
above make things work as well?
If you want the message to precede the input, you have to put the putStr on the line above, as follows:
main :: IO ()
main = do
xs <- lines <$> readFile "Cars1.txt"
putStr "Registration: "
x <- getLine
putStrLn $ case binSearch xs x of
Just n -> "Found at position " ++ show n
Nothing -> "Not found"
Alternatively,
x <- putStr "Registration: " *> getLine
or
x <- putStr "Registration: " >> getLine
would work, but they are less readable.
Finally, since you added the lazy-evaluation tag, let me add that your question is actually not about laziness, but about how the operator <* is defined, and in particular about the order in which it sequences the IO actions.

Haskell throws a parse error in a strange place

A toy example but still frustrating:
numberMapper:: IO ()
numberMapper = do codes <- forM [1 .. 4] (\num ->
do putStrLn $ "Enter a code for " ++ show num
code <- getLine
return code)
let numberCodes = zip [1 .. 4] codes
in forM numberCodes (\(num,code) ->
putStrLn $ "Got code " ++ show code ++ " for " ++ show num)
ghci tells me I have a Parse error in pattern: putStrLn and I can't figure out why it should fail to parse.
Correction:
numberMapper:: IO ()
numberMapper = do
codes <- forM [1 .. 4] $ \num -> do
putStrLn $ "Enter a code for " ++ show num
getLine
let numberCodes = zip [1 .. 4] codes
forM_ numberCodes $ \(num,code) ->
putStrLn $ "Got code " ++ show code ++ " for " ++ show num
Fix: The lines inside a do block should line up.
-- wrong
a = do codes <- something
let numberCodes = zip [1..4] codes
-- right
a = do codes <- something
let numberCodes = zip [1..4] codes
Fix 2: When using let inside a do block, don't use in.
-- wrong
func = do
let x = 17
in print x
-- right
func = do
let x = 17
print x
Fix 3: Use forM_ (which returns (), a.k.a. void) instead of forM (which returns a list).
codes <- forM [1..4] func... -- returns a list
forM_ numberCodes $ ... -- discards list, returns ()
So forM_ could (almost) be written like this:
forM_ xs f = do forM xs f
return ()
Minor change: You don't need return here:
do func1
x <- func2
return x
You can change it to the equivalent,
do func1
func2 -- value of func2 is returned
You over-indent lines in your do-blocks. Furthermore, you don't need an in for let statements in do-blocks.
This works for me:
numberMapper:: IO ()
numberMapper = do codes <- forM [1 .. 4] (\num ->
do putStrLn $ "Enter a code for " ++ show num
code <- getLine
return code)
let numberCodes = zip [1 .. 4] codes
forM numberCodes (\(num,code) ->
putStrLn $ "Got code " ++ show code ++ " for " ++ show num)
You can also structure it like this:
numberMapper:: IO ()
numberMapper = do codes <- forM [1 .. 4] $ \num ->
do putStrLn $ "Enter a code for " ++ show num
code <- getLine
return code
let numberCodes = zip [1 .. 4] codes
forM numberCodes $ \(num,code) ->
putStrLn $ "Got code " ++ show code ++ " for " ++ show num
(which lets you avoid the parentheses; alternatively, put the do at the end of the \num -> and line up subsequent statements)

Printing the values inside a tuple in Haskell

I have a list of tuples. For example: [("A",100,1),("B",101,2)]. I need to display it in a simple way. For example: "your name is: A", "Your id is: 100".
If anyone can find a solution for this, it would be a great help. Thanks in advance.
The easiest way to do this is to create a function that works for one of the elements in your list. So you'll need something like:
showDetails :: (String, Int, Int) -> String
showDetails (name, uid, _) = "Your name is:" ++ name ++ " Your ID is: " ++ show uid
Then you would apply this function to each element in the list, which means you want to use the mapping function:
map :: (a -> b) -> [a] -> [b]
So, if your list is called xs, you would want something like:
map showDetails xs
This obviously gives you a result of type [String], so you might be interested in the unlines function:
unlines :: [String] -> String
This simply takes a list of strings, and creates a string where each element is separated by a new line.
Putting this all together, then, gives you:
main :: IO ()
main = putStrLn . unlines . map showDetails $ [("A",100,1),("B",101,2)]
For a single tuple, just pattern match all the elements, and do something with them. Having a function that does that, you can use map to transform the entire list.
import Data.List (foldl')
show_tuple :: (Num a, Num b) => (String, a, b) -> String
show_tuple (name, id, something) =
"Your name is: " ++ name ++ "\n" ++
"Your ID is: " ++ (show id) ++ "\n" ++
"Your something: " ++ (show something) ++ "\n\n"
-- transforms the list, and then concatenates it into a single string
show_tuple_list :: (Num a, Num b) => [(String, a, b)] -> String
show_tuple_list = (foldl' (++) "") . (map show_tuple)
The output:
*Main Data.List> putStr $ show_tuple_list [("ab", 2, 3), ("cd", 4, 5)]
Your name is: ab
Your ID is: 2
Your something: 3
Your name is: cd
Your ID is: 4
Your something: 5
Quick and dirty solution
f (x,y,z) = "your id is " ++ (show y) ++ ", your name is " ++ (show x) ++ "\n"
main = putStrLn $ foldr (++) "" (map f [("A",100,1),("B",101,2)])
OR (by #maksenov)
main = putStrLn $ concatMap f [("A",100,1),("B",101,2)]
Please try:
get1st (a,_,_) = a
get2nd (_,a,_) = a
get3rd (_,_,a) = a
showTuples [] = ""
showTuples (x:xs) = "Your name is:" ++ show(get1st(x)) ++ " Your ID is: " ++ show(get2nd(x)) ++ "\n" ++ showTuples xs
main = do
let x = [("A",100,1),("B",101,2)]
putStrLn . showTuples $ x

Resources