题
attoparsec???????????,????????????; ????????:
#
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)
???????,????????: ????????,???????A.feed????????????????B.empty? ????????????? ????
解决方案
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 *>
.