I want to thread some state through a parser but I don't want to have a single type for state for all parts of the parser, because some types just don't make sense in some parts of the parser, but are needed in others. I could make a bigger more complicated state type with either optional values or a discriminated union, but I think that is ugly.
So I want to be able to map a function to the state of the parser.
Concretely, I want a function with the following signature
stateMap: (f:'a->'b) (p:Parser<'x,'a>) -> Parser<'x,'b>
Does such a function or operator exist in FParsec? If not, what is the idiomatic way to create it?
From checking the source code my impression is that there's no such method today and it's not that easy to implement. Parser<_>
is defined liked this:
type Parser<'Result, 'UserState> = CharStream<'UserState> -> Reply<'Result>
If there's a way to map CharStream<'a>
to CharStream<'b>
then we would reach the goal.
However investigating the source for CharStream<_>
reveals some problems:
map
for CharStream<_>
.CharStream<_>
is IDisposable
which implies FParsec
creates one instance of the CharStream<_>
at the start of parsing and this is assumed to trace through the whole parsing process. Creating a new CharStream<_>
based on an existing one and use that instead don't seem to match the design.CharStream<_>
inherits CharStream
- So CharStream
seems to do must heavy lifting when it comes to paging and CharStream<_>
is just there to pair the stream with the user state. If CharStream<_>
used a composition over inheritance we could create a new CharStream<_>
that still used the already existing CharStream
but with inheritance that's not possible. My guess is that inheritance is chosen here to avoid an extra deref and thus save a few clockcycles (performance is important for parsers).So I think the idea of composite user states sounds interesting but from what I can tell this is not supported by FParsec right now.