I was playing around with making custom list displays. The concept is really simple but I keep getting an IO() error. My code is:
displayList :: [Int] -> IO()
displayList [] = putStrLn ""
displayList (firstUnit:theRest) = putStrLn (show firstUnit ++ "\n" ++
displayList theRest)
The error code I am getting is:
• Couldn't match expected type ‘[Char]’ with actual type ‘IO ()’
• In the second argument of ‘(++)’, namely ‘(displayList theRest)’
In the first argument of ‘putStrLn’, namely
‘((show firstUnit) ++ (displayList theRest))’
In the expression:
putStrLn ((show firstUnit) ++ (displayList theRest))
The specific part of the line that is getting the error is the displayList theRest not the putStrLn ((show firstUnit) ++ part.
I think I understand what is happening which is that when displayList theRest is called in the line with the error it has the potential eventually after a few recursive calls to return an IO() type from the line displayList [] = putStrLn "" which is not supported as input in the putStrLn function. Would anyone know a way to solve this?
The Problem
The problem with your code is fairly obvious: as the compiler tells you, you're trying to concatenate a string (((show firstUnit) ++) with an IO() (the return type of your function)
Solution
The solution can take two paths: either you want a function that returns the whole string, and then prints it all in one, or just print it step by step recursively. What I mean is:
Return a string
displayList :: [Int] -> IO()
displayList = putStrLn . helper
where
helper :: [Int] -> String
helper [] = ""
helper (n:ns) = show n ++ "\n" ++ helper ns
This approach works fine, but I believe it's not tidy nor clear.
Better version
displayList' :: [Int] -> IO()
displayList' [] = putStrLn ""
displayList' (n:ns) = putStrLn (show n) >> displayList' ns
I think you can see how this version is easier to read. Also note that print :: Show a => a -> IO() works exactly as putStrLn . show
Related
I tried break line using \n, putStrLn and print but nothing works.
When I use \n the result only concatenates the strings, and when I use putStrLn or print I receive a type error.
Output for \n:
formatLines [("a",12),("b",13),("c",14)]
"a...............12\nb...............13\nc...............14\n"
Output for putStrLn:
format.hs:6:22:
Couldn't match type `IO ()' with `[Char]'
Expected type: String
Actual type: IO ()
In the return type of a call of `putStrLn'
In the expression:
putStrLn (formatLine ((fst x), (snd x)) ++ formatLines xs)
In an equation for `formatLines':
formatLines (x : xs)
= putStrLn (formatLine ((fst x), (snd x)) ++ formatLines xs)
Failed, modules loaded: none.
the output for print is the same as that of putStrLn
Here is my code:
formatLine :: (String,Integer) -> String
formatLine (s, i) = s ++ "..............." ++ show i
formatLines::[(String,Integer)] -> String
formatLines [] = ""
formatLines (x:xs) = print (formatLine ((fst x), (snd x)) ++ formatLines xs)
I understand the reason of the error for print and putStrLn but i have no idea how fix it.
Split your code in two parts.
One part simply constructs the string. Use "\n" for newlines.
The second part takes the string and applies putStrLn (NOT print) to it. The newlines will get printed correctly.
Example:
foo :: String -> Int -> String
foo s n = s ++ "\n" ++ show (n*10) ++ "\n" ++ s
bar :: IO ()
bar = putStrLn (foo "abc" 42)
-- or putStr (...) for no trailing newline
baz :: String -> IO ()
baz s = putStrLn (foo s 21)
If you use print instead, you'll print the string representation, with quotes and escapes (like \n) inside it. Use print only for values that have to be converted to string, like numbers.
Also note that you can only do IO (like printing stuff) in functions whose return type is IO (something).
You need to print the results to output.
This is an IO action, and so you cannot have a function signature ending with -> String. Instead, as #chi points out, the return type should be IO (). Further, since you have the function to generate formatted string already, all you need is a function to help you map the printing action over your input list. This you can do using mapM_, like so:
formatLines::[(String,Integer)] -> IO ()
formatLines y = mapM_ (putStrLn . formatLine) y
Demo
I've written a basic recursive function:
bibliography_rec :: [(String, String, Int)] -> String
bibliography_rec [] = ""
bibliography_rec (x:xs) = (citeBook x) ++ "\n" ++ (bibliography_rec xs)
citeBook simply reformats the tuple into a String.
When run with this input:
ghci> bibliography_rec [("Herman Melville", "Moby Dick", 1851),("Georgy Poo", "Alex Janakos", 1666)]
It produces:
"Moby Dick (Herman Melville, 1851)\nAlex Janakos (Georgy Poo, 1666)\n"
I need line by line printing so I used this:
bibliography_rec (x:xs) = putStr ((citeBook x) ++ "\n" ++ (bibliography_rec xs))
My problem is my output NEEDS to be of type String NOT IO ()
I've been stuck on this for way too long so any help is great!
Looks like you're already there, you just need to putStrLn the string instead of printing it (which is what ghci does by default). print runs its argument through show first, so it will quote the escape characters like "\n".
ghci> putStrLn $ bibliography_rec [...]
how can help me getting this main function running, dont know how to do this, i made a small example:
tuple :: String -> (Bool, String)
tuple x = (True, x)
getStr :: String
getStr = "test"
main = do
putStrLn snd (tuple getStr)
putStrLn "End"
You're missing parenthesis around the application of snd:
main = do
putStrLn (snd (tuple getStr))
putStrLn "End"
I don't understand this type error:
Couldn't match expected type `[t0]' with actual type `IO ()'
In the return type of a call of `printv'
In a stmt of a 'do' expression: px <- printv x
In the expression:
do { px <- printv x;
sep <- print ", ";
rest <- prints xs;
return (px : sep : rest) }
From:
data Value = IntValue Int
| TruthValue Bool
deriving (Eq, Show)
printv :: Value -> IO()
printv (IntValue i) = print i
printv (TruthValue b) = print ("boolean" ++ show b)
prints :: [Value] -> [IO()]
prints [] = []
prints (x:xs) = do px <- printv x
sep <- print ", "
rest <- prints xs
return (px:sep:rest)
It looks to me like every element (px) is converted into an IO() action, and then that is added to a list of the same things, thus producing an [IO()] list.
What am I missing here? Converting it to a list of strings, by removing the print's, works fine.
You're missing the return on the [] case of prints:
prints [] = return []
However, your prints is very strange. It returns a [()], because print is outputting strings to the console, not returning them.
Do you mean to return strings from your printv function?
Since you're trying to pretty print a syntax tree, here's roughly the right way to do it:
Use pretty-printing combinators
Use a pretty typeclass
Like so:
import Text.PrettyPrint
import Data.List
data Value
= VInt Int
| VBool Bool
deriving (Eq, Show)
class Pretty a where
pretty :: a -> Doc
instance Pretty Value where
pretty (VInt i) = int i
pretty (VBool b) = text "Boolean" <+> text (show b)
draw :: [Value] -> String
draw = intercalate ", " . map (render.pretty)
main = putStrLn $ draw [VInt 7, VBool True, VInt 42]
Running it:
*A> main
7, Boolean True, 42
Take a closer look at the type of your function:
prints :: [Value] -> [IO()]
But if we now take a look at prints [] = [], this can't match, because the type of that one is
prints :: [t] -> [a]
Therefore, you missed using prints [] = return [], to make it work.
If you're not evaluating an IO action, you don't need a do block. Just treat IO () as a normal type.
prints (x:xs) = printv x : print ", " : prints xs
You don't want prints to return an array of IO actions. You want it to return a single IO action that represents each of the IO actions bound together. Something like:
prints xs = mapM_ (\x -> printv x >> putStr ", ") xs
Except that I don't think the new lines are going to end up where you want them.
Look at the documentation for mapM and sequence for more information. In particular, the implementation of sequence is probably similar to what you're trying to do.
However, I would really recommend that instead doing all the work in an IO function, you should write a pure function to render the textual format you want, and then just print that. In particular, it seems that an instance of Show for Value would be appropriate.
instance Show Value where
show (IntValue i) = show i
show (TruthValue b) = "boolean " ++ show b
That way you can just call print value rather than printv value, and if you really wanted to you could define prints as follows.
import Data.List
prints :: (Show a) => [a] -> IO ()
prints = putStrLn . intercalate ", " . map show`.
I have the following functions in Haskell that must print the sales of weeks. Each sale in a new line. But it is not working the way i expect it to. The problem i have is the newline character '\n'.
Code:
printWeeks :: Int->String
printWeeks 0 = printWeek 0
printWeeks x = printWeeks(x-1) ++ printWeek x
printWeek :: Int->String
printWeek x = show(x) ++ " " ++ stars (sales x) ++ "'\n'"
I have tried many ways but the new line character is not working as expected. Everything is printed on the same line whichis not what i want.
Need help?
thanks
UPDATE
The following is not working because of compile errors. The errors comes from the second line of formatLines. The type decalaration is causing errors. Need help here
formatLine :: (Name,Price)->IO()
formatLine (a,b) = putStrLn (a ++ dots ++ p)
where
x=(length a)
p=(formatPence b)
y=length p
z=lineLength-(x+y)
dots = printDots z
formatLines :: [(Name,Price)]->IO()
formatLines []= ""
formatLines (a:x) = formatLines x ++ formatLine a
You should use ++ "\n" to append a newline to the output; your current code will add a ', then a newline, then another '.
As #marcog points out, be sure to use putStr to print it out (or don't append the newline at all and use putStrLn). Example:
Hugs> putStr (show 4 ++ "\n")
4
Hugs> putStrLn (show 4 ++ "\n")
4
Hugs> print (show 4 ++ "\n")
"4\n"
(Note that the Hugs interpreter adds extra newlines after each output.)
You are probably printing the string using print x, which is equivalent to putStrLn (show x). show x is converting the newlines into readable characters \ and n. You need to use putStrLn x instead, or putStr x if you don't want to append a newline to the end of the string.
You should also remove the single quotes you have around the newline, unless that was intentional.
It's a bit of a riddle why so much action is happening under the heading of IO. This is maybe a little verbose. I couldn't tell where lineLength was coming from so I made it a parameter.
formatLine :: Int -> (Name,Price) -> String
formatLine linelength (name, price) = name ++ dotfill ++ showprice
where
showprice :: String
showprice = formatPence price
extra :: Int
extra = linelength - length (name ++ showprice)
dotfill :: String
dotfill = replicate extra '.'
formatLines :: Int -> [(Name, Price)] -> String
formatLines linelength []= ""
formatLines linelength (first:rest) =
(formatLine linelength first ++ "\n") ++ formatLines linelength rest
standardPrint :: [(Name, Price)] -> IO ()
standardPrint listing = putStrLn (formatLines 50 listing)
fileAwayPrices :: FilePath -> [(Name,Price)] -> IO()
fileAwayPrices filename listing = writeFile filename (formatLines 70 listing)
testlist :: [(Name,Price)]
testlist = [("oats",344),("barley", 299),("quinoa",599)]
-- *Main> standardPrint testlist
-- oats...........................................344
-- barley.........................................299
-- quinoa.........................................599
type Name = String
type Price = Integer
formatPence n = show n
Re your update: your type declaration is correct, it's the rest of formatLines that's wrong.
formatLines :: [(Name,Price)]->IO()
formatLines [] = return ()
formatLines (a:x) = formatLines x >> formatLine a
A more concise way of writing that is
formatLines :: [(Name,Price)]->IO()
formatLines = mapM_ formatLine . reverse