문제

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.

도움이 되었습니까?

해결책

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 bars. 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.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top