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
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 *>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With