1

I'm using the Sprache library, which allows parsers to be built using LINQ. However, sometimes I need to parse something and then discard the result. For example:

from key in Identifier
from __ws1 in OptionalWhitespace
from __setter in PropertySetter
from __ws2 in OptionalWhitespace
from value in PropertyValue
select ...

I don't need any of the three variables prefixed with __, so it seems unnecessary to pollute the namespace with them.

Is there any way I can perform the LINQ query and discard those three results?

Aaron Christiansen
  • 11,584
  • 5
  • 52
  • 78
  • I'm guessing you have a where or a join in there somewhere to link the tables together? Can you expand your example a little so we can see how the other tables are used? – Alex KeySmith Dec 15 '17 at 09:03
  • @Alex KeySmith This isn't a database query, it's a parser rule built using the Sprache library. – Aaron Christiansen Dec 15 '17 at 09:05
  • Sorry my use of the term "table" probably didn't make sense. I wasn't sure how your other _collections_ are being used. I'm guessing your trying to get at ProperyValue which is somehow nested in Identifier? Can you expand your example a little bit as it'll help understand your problem domain. – Alex KeySmith Dec 15 '17 at 09:07
  • Oh I see, looking at the examples on the sprache site, I'm guessing those are appearing in the select statement? Or not as it might be the case. – Alex KeySmith Dec 15 '17 at 09:10
  • It looks like in their examples, it's just a way Sprache is building up expressions even if you don't need them. Kinda weird, probably my lack of deep understanding of expressions, that even though it's not used in a select or where or apparently anything in ones own code, that it's still used. – Alex KeySmith Dec 15 '17 at 09:14

3 Answers3

2

Not having in depth knowledge of Sprache, I've super simplified the example to understand the problem domain.

Take this example of multiple from statements (using LinqPad), where we're only interested in one of the values of the from. So in this arbitrary example, we want to know all the combinations of people and cake, but only interested in the names of the cake.

var people = new List<string> {
    "Billy", "Jimmy"
};

var cake = new List<string> {
    "Carrot Cake", "Chocolate Cake"
};

(from p in people
from c in cake
select new
{
    c
}).Dump();

Multiple from statements can be thought of as nested foreach loops which end up being a cross join (as discussed here LINQ Join with Multiple From Clauses)

So let's assume that this is the intention of the Sprache's authors, if we try to rewrite this in the fluent syntax (as discussed here: https://codeblog.jonskeet.uk/2011/01/28/reimplementing-linq-to-objects-part-41-how-query-expressions-work/) it ends up being a SelectMany().

We end up with something like:

people.SelectMany(p => cake, (p, c) => new { c }).Dump();

Which you still end up with the "person" paramater.

So I'd suggest that to keep to Sprache's intent there isn't a way of not having the __ statements. Unless perhaps building up the expression tree yourself, but I can't imagine that would be productive.

Alex KeySmith
  • 16,657
  • 11
  • 74
  • 152
  • 1
    You've understood the problem perfectly - it's a shame there's no way to discard these values. I suppose it's not a common thing to want to do. Thanks for answering – Aaron Christiansen Dec 15 '17 at 10:17
1

In this particular case you can use Then to discard all irrelevant parts at once:

var parser =
    from key in Identifier
    from _ in OptionalWhitespace
        .Then(_ => PropertySetter)
        .Then(_ => OptionalWhitespace)
    from value in PropertyValue
    select new
    {
        key,
        value
    };

But it won't help in the generic case if the irrelevant parts don't immediately follow each other.

yallie
  • 2,200
  • 1
  • 28
  • 26
1

Sprache implements LINQ query methods for monadic binding of parser functions like a parser combinator. (LINQ can be used for a lot more interesting stuff than querying EF DbSets.) Sadly discard is not supported for LINQ query range variables (yet) so your '_' range variables will collide in scope. I usually name range variables I don't need like '_N' (where N in [1..]) when I have more than one in a query and hope to remember to use proper discards when they get supported.