parsec error in haskelwiki tutorial - haskell

I was following the code in http://www.haskell.org/haskellwiki/Hitchhikers_guide_to_Haskell, and the code (in chapter 2) gives an error. There is no author name/email mentioned with the tutorial, so I am coming here for advise. The code is below, and the error occurs on the "eof" word.
module Main where
import Text.ParserCombinators.Parsec
parseInput =
do dirs <- many dirAndSize
eof
return dirs
data Dir = Dir Int String deriving Show
dirAndSize =
do size <- many1 digit
spaces
dir_name <- anyChar `manyTill` newline
return (Dir (read size) dir_name)
main = do
input <- getContents
putStrLn ("Debug: got inputs: " ++ input)

That tutorial was written a long time ago, when parsec was simple. Nowadays, since parsec-3, the library can wrap monads, so you now have to specify (or otherwise disambiguate) the type to use at some points. This is one of them, giving eof e.g. the expression type signature eof :: Parser () makes it compile.

Related

Trouble shoot a Haskell program

Can anyone tell me what is the problem with this Haskell program
import Control.Monad
import Data.Char
main = do
contents <- getContents
putStrLn $ contents
putStr $ "shortLinesOnly version is " ++ (shortLinesOnly contents)
putStr $ "printOnlyLessChars version is " ++ (printOnlyLessChars contents)
shortLinesOnly :: String -> String
shortLinesOnly input =
let allLines = lines input
shortLines = filter (\line -> length line < 10) allLines
result = unlines shortLines
in result
--------------------the other way of doing this is -----------------
printOnlyLessChars contents = unlines $ filter (\a -> length a < 10) $ lines $ contents
The program works fine, but it fails when I try to print the contents (line 5). Why is it having problems printing the string via putStrLn
The error message I get is
* Couldn't match expected type `(String -> IO ())
-> t0 -> IO String'
with actual type `IO String'
* The function `getContents' is applied to one argument,
but its type `IO String' has none
In the expression: getContents putStrLn
Thanks,
This is the line that you need to focus on:
In the expression: getContents putStrLn
This is haskell showing you how it views your code, but your code doesn't look like that. This is almost always an indentation error. Check that you don't have an extra space or a tab where it doesn't belong.
As a suggestion when reading haskell type error messages there are three places to look, and you should scan all of them before fixating on a single one:
The type signature information -- do your types really match?
The expression information -- does the expression the compiler sees match your expectations, or do you need to add $ or parens
Is there a typo or indentation problem.
I frequently feel my brain starting to overheat as I try to read through a really messy Couldn't match expected type so before I get too upset over trying to read that part of the error message I carefully check the In the expression: part to make sure that there is an easy to fix issue with how I entered the code.

Read a single int from a file and print it

How do I create a program that reads a line from a file, parse it to an int and print it(ignoring exceptions of course). Is there anything like "read" but for IO String?
I've got this so far but I couldn't get around the IO types:
readFromFile = do
inputFile <- openFile "catalogue.txt" ReadMode
isbn <- read( hGetLine inputFile)
hClose inputFile
You can specify the type explicitly, change the read line to
isbn <- fmap read (hGetLine inputFile) :: IO Int
As hGetLine inputFile is of type IO String, you should use fmap to get "inside" to read as an Int.
You can use the readFile function to convert your file to a string.
main = do
contents <- readFile "theFile"
let value = read $ head $ lines contents::Int
print value
You should add better error detection, or this program will fail if there isn't a first line, or if the value is malformed, but this is the basic flow....
First, observe that reading stuff and then immediately printing it can result in mysterious errors:
GHCi, version 8.0.0.20160421: http://www.haskell.org/ghc/ :? for help
Prelude λ read "123"
*** Exception: Prelude.read: no parse
The reason is that you don't specify what type you want to read. You can counter this by using type annotations:
Prelude λ read "123" :: Integer
123
but it is sometimes easier to introduce a little helper function:
Prelude λ let readInteger = read :: String -> Integer
Prelude λ readInteger "123"
123
Now to the main problem. read( hGetLine inputFile) doesn't work because hGetLine inputFile returns and IO String and read needs a String. This can be solved in two steps:
line <- hGetLine inputFile
let isbn = readInteger line
Note two different constructs <- and let .. =, they do different things. Can you figure out exactly what?
As shown in another answer, you can do it in a less verbose manner:
isbn <- fmap readInteger (hGetLine inputFile)
which is great if you do a simple thing like read. But it is often desirable to explicitly name intermediate results. You can use <- and let .. = constructs in such cases.

Haskell parse error (possibly incorrect indentation or mismatched brackets)

I'm trying to write an IO program that reverses text, but it just can't work. I've tried to adjust the indention of reverse function, still, it didn't work.
The full error message is :
reverse.hs:11:1:
parse error (possibly incorrect indentation or mismatched brackets)
--reverse a file
module Main where
import System.IO
main = do
putStrLn "reading a file..."
theInput <- readFile "input.txt"
writeFile "output.txt" $ reverse` theInput
reverse` :: String -> String
reverse` [] = []
reverse` (_:xs) = reverse xs : _
You can't use a backtick as part of an identifier. It's a syntactic token for using a function name as an inline operator (e.g. compare `on` thing). You probably wanted a normal tick mark (').

Parsec - error "combinator 'many' is applied to a parser that accepts an empty string"

I'm trying to write a parser using Parsec that will parse literate Haskell files, such as the following:
The classic 'Hello, world' program.
\begin{code}
main = putStrLn "Hello, world"
\end{code}
More text.
I've written the following, sort-of-inspired by the examples in RWH:
import Text.ParserCombinators.Parsec
main
= do contents <- readFile "hello.lhs"
let results = parseLiterate contents
print results
data Element
= Text String
| Haskell String
deriving (Show)
parseLiterate :: String -> Either ParseError [Element]
parseLiterate input
= parse literateFile "(unknown)" input
literateFile
= many codeOrProse
codeOrProse
= code <|> prose
code
= do eol
string "\\begin{code}"
eol
content <- many anyChar
eol
string "\\end{code}"
eol
return $ Haskell content
prose
= do content <- many anyChar
return $ Text content
eol
= try (string "\n\r")
<|> try (string "\r\n")
<|> string "\n"
<|> string "\r"
<?> "end of line"
Which I hoped would result in something along the lines of:
[Text "The classic 'Hello, world' program.", Haskell "main = putStrLn \"Hello, world\"", Text "More text."]
(allowing for whitespace etc).
This compiles fine, but when run, I get the error:
*** Exception: Text.ParserCombinators.Parsec.Prim.many: combinator 'many' is applied to a parser that accepts an empty string
Can anyone shed any light on this, and possibly help with a solution please?
As sth pointed out many anyChar is the problem. But not just in prose but also in code. The problem with code is, that content <- many anyChar will consume everything: The newlines and the \end{code} tag.
So, you need to have some way to tell the prose and the code apart. An easy (but maybe too naive) way to do so, is to look for backslashes:
literateFile = many codeOrProse <* eof
code = do string "\\begin{code}"
content <- many $ noneOf "\\"
string "\\end{code}"
return $ Haskell content
prose = do content <- many1 $ noneOf "\\"
return $ Text content
Now, you don't completely have the desired result, because the Haskell part will also contain newlines, but you can filter these out quite easily (given a function filterNewlines you could say `content <- filterNewlines <$> (many $ noneOf "\\")).
Edit
Okay, I think I found a solution (requires the newest Parsec version, because of lookAhead):
import Text.ParserCombinators.Parsec
import Control.Applicative hiding (many, (<|>))
main
= do contents <- readFile "hello.lhs"
let results = parseLiterate contents
print results
data Element
= Text String
| Haskell String
deriving (Show)
parseLiterate :: String -> Either ParseError [Element]
parseLiterate input
= parse literateFile "" input
literateFile
= many codeOrProse
codeOrProse = code <|> prose
code = do string "\\begin{code}\n"
c <- untilP (string "\\end{code}\n")
string "\\end{code}\n"
return $ Haskell c
prose = do t <- untilP $ (string "\\begin{code}\n") <|> (eof >> return "")
return $ Text t
untilP p = do s <- many $ noneOf "\n"
newline
s' <- try (lookAhead p >> return "") <|> untilP p
return $ s ++ s'
untilP p parses a line, then checks if the beginning of the next line can be successfully parsed by p. If so, it returns the empty string, otherwise it goes on. The lookAhead is needed, because otherwise the begin\end-tags would be consumed and code couldn't recognize them.
I guess it could still be made more concise (i.e. not having to repeat string "\\end{code}\n" inside code).
I haven't tested it, but:
many anyChar can match an empty string
Therefore prose can match an empty string
Therefore codeOrProse can match an empty string
Therefore literateFile can loop forever, matching infinitely many empty strings
Changing prose to match many1 characters might fix this problem.
(I'm not very familiar with Parsec, but how will prose know how many characters it should match? It might consume the whole input, never giving the code parser a second chance to look for the start of a new code segment. Alternatively it might only match one character in each call, making the many/many1 in it useless.)
For reference, here's another version I came up with (slightly expanded to handle other cases):
import Text.ParserCombinators.Parsec
main
= do contents <- readFile "test.tex"
let results = parseLiterate contents
print results
data Element
= Text String
| Haskell String
| Section String
deriving (Show)
parseLiterate :: String -> Either ParseError [Element]
parseLiterate input
= parse literateFile "(unknown)" input
literateFile
= do es <- many elements
eof
return es
elements
= try section
<|> try quotedBackslash
<|> try code
<|> prose
code
= do string "\\begin{code}"
c <- anyChar `manyTill` try (string "\\end{code}")
return $ Haskell c
quotedBackslash
= do string "\\\\"
return $ Text "\\\\"
prose
= do t <- many1 (noneOf "\\")
return $ Text t
section
= do string "\\section{"
content <- many1 (noneOf "}")
char '}'
return $ Section content

Parsec error - try doesn't seem to work

I'm currently using the Text.Parsec.Expr module to parse a subset of a scripting language.
Basically, there are two kinds of commands in this language: Assignment of the form $var = expr and a Command of the form $var = $array[$index] - there are of course other commands, but this suffices to explain my problem.
I've created a type Command, to represent this, along with corresponding parsers, where expr for the assignment is handled by Parsec's buildExpressionParser.
Now, the problem. First the parsing code:
main = case parse p "" "$c = $a[$b]" of
Left err -> putStrLn . show $ err
Right r -> putStrLn . show $ r
where p = (try assignment <|> command) <* eof -- (1)
The whole code (50 lines) is pasted here: Link (should compile if you've parsec installed)
The problem is, that parsing fails, since assignment doesn't successfully parse, even though there is a try before. Reversing the parsing order (try command <|> assignment) solves the problem, but is not possible in my case.
Of course I tried to locate the problem further and it appears to me, that the problem is the expression parser (build by buildExpressionParser), since parsing succeeds if I say expr = fail "". However I can't find anything in the Parsec sources that would explain this behaviour.
You parser fails because in fact assigment does succeeds here consuming $c = $a (try it with plain where p = assignment). Then there is supposed to be eof (or the rest of expr from assigment) hence the error. It seems that the beggining of your 'command' is identical to your 'assignment' in the case when 'assignment''s argument is just a var (like $c = $a).
Not sure why you can't reverse command and assignment but another way to make this particular example work would be:
main = case parse p "" "$c = $a[$b]" of
Left err -> putStrLn . show $ err
Right r -> putStrLn . show $ r
where p = try (assignment <* eof) <|> (command <* eof)

Resources