datalust / superpower

A C# parser construction toolkit with high-quality error reporting
Apache License 2.0
1.05k stars 98 forks source link

[Question] TextParser<T> covariance support? #112

Closed CrispyDrone closed 4 years ago

CrispyDrone commented 4 years ago

Hello,

I'm trying out the library for the first time after having watched your great talk about "Building a parser in C#, from first principles".

While writing the following code, I noticed this didn't compile with the following error:

Cannot implicitly convert type 'Superpower.TextParser<ParserCombinators.Models.MapFilter>' 
to 'Superpower.TextParser<ParserCombinators.Models.Filter>'
    internal static class FilterGrammar
    {
        private static readonly TextParser<MapFilter> MapFilter =
            from prefix in Character.EqualTo('m')
            from colon in Character.EqualTo(':')
            from maps in Maps
            select new MapFilter(maps);

        private static readonly TextParser<Map[]> Maps =
            from maps in Map.AtLeastOnceDelimitedBy(Character.In(','))
            select maps;

        private static readonly TextParser<Map> Map =
            from mapName in ValidMapNameCharacter
                .AtLeastOnceDelimitedBy(Character.WhiteSpace)
            select new Map(new string(mapName));

        private static readonly TextParser<char> ValidMapNameCharacter =
            from @char in Character
                .LetterOrDigit
                .Or(Character.In('.', '*'))
            select @char;

        internal static readonly TextParser<Filter> Filter =
            // MapFilter.Select(mapFilter => mapFilter as Filter);
            MapFilter;
    }

MapFilter is a class that derives from Filter.

In Sprache I see that a parser is defined as public delegate IResult<T> Parser<out T>(IInput input); However in superpower a text parser is defined as public delegate Result<T> TextParser<T>(TextSpan input);

If I'm correct this means that in superpower I can't make use of covariance which is why the code won't compile.

Is there specific reason for this?

Cheers

nblumhardt commented 4 years ago

Hi! Glad you enjoyed my talk - thanks for the note, much appreciated.

The reason Superpower parsers aren't covariant is that structs in C# don't support it. A Result<T> struct can't declare T as out the way an interface can. Superpower uses struct results to reduce allocations - there are a lot of results generated during the parsing process - and covariance was sacrificed as a result, unfortunately.

Cheers, Nick

CrispyDrone commented 4 years ago

Hi Nick,

Thanks for your quick response. I see, I wasn't actually aware that you could only make interfaces or delegates co- and contravariant.

And I think the lack of it can easily be circumvented by using for example: MapFilter.Select(mapFilter => mapFilter as Filter); which isn't that cumbersome.

I'm looking forward to experimenting much more with this library!