6 ms·
So the main performance penalty is that you want to parse foobar | foobaz As fooba(r|z) But this requires you to analyse your parsers. Virtually all
by Tarean 8y ago
So the main performance penalty is that you want to parse
foobar | foobaz
As
fooba(r|z)
But this requires you to analyse your parsers. Virtually all parser combinator libraries in haskell are monadic and most others follow suit. This means you can parse turing complete languages and parsers are very pleasant to write but also all predicates are undecidable and therefore you can't optimize well.
There are a couple ways around this:
- rewrite into non-turing-complete parsers when possible using heuristics. Ghc supports this, the original motivation was implicit parallelism for facebooks anti-abuse infrastructure
- use a non-monadic dsl. This either makes coding more awkward or forces you to reimplement a bunch of things like if statements
- if you really need performance and error messages use a parser generator instead of an embedded dsl
Ghc inlines functions incredibly aggressively so the higher order combinators don't have a huge cost. Doing this in a jit compiled language will be more expensive. There are also tradeoffs in the primitives you use - you could only have a primitive to check a single char and then use it in a loop to check strings. But even when inlined you won't beat a fine tuned string comparison function this way.
- abecedarius 8y agoRight, I think you only need monadic bind for semantic predicates. Ordinary semantic actions (i.e. ones that don't feed back into deciding how to parse the rest of the input) can fit smoothly into a parser combinator design without monads, but AFAIK that's not how it's usually done.