I’ve been thinking about the solution I proposed to @Pamplemousse’s #91 and I think it can be taken further. I'd love some feedback
There are lots of combinators that can be considered to be modified versions of other combinators. For example, the many combinator has the versions:
And each of those often has multiple optional parameters for tweaking their functionality. This is taken from traditional parser combinator libraries like Parsec of course. But it does have downsides.
- Documentation and functionality are duplicated.
- Adding extra functionality means overburdening individual combinator interfaces:
- Adding more optional parameters
- Adding another similar combinator with a different name
- It can be hard to tell which extra functionality is exposed via optional parameters and which is just done using other combinators.
A good example of the problem was in #35, where the user wanted to capture the separators in manySepBy. But that can't be a default since lots of people probably don't want that. So either adding an optional parameter or adding another parser, both having downsides.
What if instead of doing that, we have a single many combinator that supports lots of different modifiers to mix and match different additional functionality?
How it would work
Let’s take the many combinator as an example. First we’d have the basic many combinator. This combinator could be modified with features such as separators, minimum/maximum matches, and so on. This would be done as instance methods defined on individual combinators.
For example, many could have the methods:
till(tillParser)
min(minMatches)
max(maxMatches)
sep(sepParser)
So a full instantiation of the modified combinator could look like this:
const manyTillnewline1to3 = many().till(newline()).min(1).max(3).sep(",")
Specific operators could be shared among all combinators and parsers, like caseSense which can enable/disable case sense in the whole parser (#91).
You would still apply combinators using pipe to actual parsers. For example,
string("abc").pipe(
manyTillnewline1to3,
then(string("abc"))
)
Advantages
The main advantage is the branching API. It groups extra features around core functionality instead of having a giant list of combinators.
Disadvantages
- Departs from the interface of libraries like Parsec.
- Unusual. I haven’t seen any combinator-based library do this.
- Will involve deprecating existing parsers to maintain uniformity. Or something along those lines. Maybe a separate import.
Method of rollout
If people like the idea, I’d like to do a gradual rollout. I think just replacing the interface with one major version change is too much. It should be spread over 1 – 3 version upgrades.
Feedback
What do you think?
- Do you like it?
- Do you see any problems?
- Any tweaks to the design?
- Do you know anywhere this kind of API was used?
I’ve been thinking about the solution I proposed to @Pamplemousse’s #91 and I think it can be taken further. I'd love some feedback
There are lots of combinators that can be considered to be modified versions of other combinators. For example, the
manycombinator has the versions:manyTillmany1manySepByAnd each of those often has multiple optional parameters for tweaking their functionality. This is taken from traditional parser combinator libraries like Parsec of course. But it does have downsides.
A good example of the problem was in #35, where the user wanted to capture the separators in
manySepBy. But that can't be a default since lots of people probably don't want that. So either adding an optional parameter or adding another parser, both having downsides.What if instead of doing that, we have a single
manycombinator that supports lots of different modifiers to mix and match different additional functionality?How it would work
Let’s take the
manycombinator as an example. First we’d have the basicmanycombinator. This combinator could be modified with features such as separators, minimum/maximum matches, and so on. This would be done as instance methods defined on individual combinators.For example,
manycould have the methods:till(tillParser)min(minMatches)max(maxMatches)sep(sepParser)So a full instantiation of the modified combinator could look like this:
Specific operators could be shared among all combinators and parsers, like
caseSensewhich can enable/disable case sense in the whole parser (#91).You would still apply combinators using
pipeto actual parsers. For example,Advantages
The main advantage is the branching API. It groups extra features around core functionality instead of having a giant list of combinators.
Disadvantages
Method of rollout
If people like the idea, I’d like to do a gradual rollout. I think just replacing the interface with one major version change is too much. It should be spread over 1 – 3 version upgrades.
Feedback
What do you think?