If you need optimized implementations, you can define them using the new Inline helper class. The file Doc/misc/removed‐many‐variants.fs contains optimized definitions for all removed variants.

If you previously used these variants, you can easily define them in your own code using the |>> combinator, as documented in the reference documentation for the previous version. For example:

The behaviour change made the skip variants of manyChar and manyCharsTill obsolete, since e.g. skipManyChars would do exactly the same as skipMany cp . Hence, the skip variants have been removed.

The behaviour of all variants of manyChars and manyCharsTill has slightly changed. Now manyChars cp is equivalent to many cp , except that it returns a string instead of char list. Previously, manyChars cp behaved like many ( attempt cp) , i.e. it automatically backtracked if the char parser had failed after consuming input. The same change has been made to the behaviour of all other variants of manyChars and manyCharsTill . The new behaviour is more consistent with the rest of the libary and allows a faster implementation with the new low‐level API.

Various methods from the old CharStream.Iterator and State types have been renamed in the new CharStream class and have new signatures:

Previously parsers were implemented as functions operating on an immutable parser state in the form of a State instance. A parser function received a State instance as the input and returned a State instance as part of its return value. Since State instances were immutable, a parser function had to create a new State instance to advance the input stream, e.g. by calling state.Advance(2) .

This architecture was motivated by the desire to provide an API as “functional” as possible, an API that shields users from the underlying imperative/mutable nature of input streams. When FParsec originally started as a relatively close port of Haskell’s Parsec library, this design felt like a natural fit for a functional parser library. However, later, when FParsec moved away from its Parsec roots (to improve performance and provide more features), it became increasingly clear that the immutable CharStream ‐ State ‐design was the main obstacle preventing FParsec from reaching the performance of hand‐optimized recursive‐descent parsers.

Initial tests with some quick prototypes revealed that the allocation and garbage collection of temporary State instances took up to 50% or more of the run time of typical parsers – even though the State class was already heavily optimized. These tests also indicated that consolidating the stream and state classes into a classical imperative stream class simplified the overall library implementation and made the library source code more accessible to new users.

The main drawback of the API change is that it requires modifications to practically all low‐level parser code. Another drawback is that backtracking is slightly less convenient with the new low‐level API (as the parser state has to be explicitly saved and restored, while previously one could just continue with an old state instance).