问题
I want to write a parser for a comma separated pair of values in angle brackets. I got it to work with the following approach:
pair p1 p2 = do
x1 <- p1
comma
x2 <- p2
return (x1, x2)
data Foo = Foo (Bar, Bar)
foo :: Parser Foo
foo = Foo <$> (angles $ pair bar bar)
However I would prefer the Foo constructor to take two parameter rather than a tuple:
data Foo = Foo Bar Bar
What is the best way to write such a parser? Ideally I would like to reuse standard Parsec parsers such a angles
and use applicative as much as possible.
回答1:
What is the best way to write such a parser? Ideally I would like to reuse standard Parsec parsers such a angles and use applicative as much as possible.
In applicative style, your parser would be
foo = angles $ Foo <$> bar <* comma <*> bar
Going inside out, a bar
is parsed, then a comma
, which is discarded, and another bar
, then the constructor Foo
is applied to the two parsed bar
s. Finally, all is wrapped into the angles
combinator, so that a string of the form
< bar , bar >
is parsed (bar
should probably consume trailing whitespace).
Combining parsers ignoring the result of one with the *>
and <*
applicative combinators eliminates the need for the pair
combinator and easily generalises to constructors taking an arbitrary number of arguments.
As C.A. McCann mentioned in a comment, the (<$)
combinator (which is part of GHC's implementation of the Functor
class, with the default implementation (<$) = fmap . const
; but it's not part of the language standard) is useful too, if you want to ignore a leading token. Using that, you can write
Foo <$ ignoreMe <*> bar <* comma <*> baz
which is nicer than using parentheses
Foo <$> (ignoreMe *> bar) <* comma <*> baz
or pure
,
pure Foo <* ignoreMe <*> bar <* comma <*> baz
as would be required in some form without it.
来源:https://stackoverflow.com/questions/14067552/applicative-style-parser-for-constructor-with-two-arguments