Question

attoparsec was suggested to me for parsing a file, now I must to understand how to use it; somebody gave me this piece of code:

#

type Environment = M.Map String String 
import Data.Attoparsec (maybeResult)
import qualified Data.Attoparsec.Char8 as A
import qualified Data.ByteString.Char8 as B
environment :: A.Parser Environment
environment = M.fromList <$> A.sepBy entry A.endOfLine
parseEnvironment = maybeResult .flip A.feed B.empty . A.parse environment
spaces = A.many $ A.char ' '
entry = (,) <$> upTo ':' <*> upTo ';'
upTo delimiter = B.unpack <$> A.takeWhile (A.notInClass $ delimiter : " ")
                      <* (spaces >> A.char delimiter >> spaces)

that works very well, but I do not know why: what the reason of using flip, is it not easier to put the argument of A.feed in a different order? and why is there B.empty? is there some tutorial about that I can study? thanks in advance

Was it helpful?

Solution

There's an explanation of the need for feed in the answers to this StackOverflow question. As Bryan O'Sullivan (the creator of Attoparsec) says there:

If you write an attoparsec parser that consumes as much input as possible before failing, you must tell the partial result continuation when you've reached the end of your input.

You can do this by feeding it an empty bytestring.

I'll admit that I wrote the code in question, and I actually didn't use pointfree in this case. Simple composition just makes sense to me here: you run the parser (A.parse environment), you tell it you're done (flip A.feed B.empty), and you convert to a Maybe as a kind of basic error handling (maybeResult). In my opinion this feels cleaner than the pointed version:

parseEnvironment b = maybeResult $ A.feed (A.parse environment b) B.empty

The rest is I think fairly idiomatic applicative parsing, although I'm not sure why I would have used >> instead of *>.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top