Champion "Query-foreach (do
query termination)"
#8930
Replies: 32 comments 1 reply
-
I've updated the issue, above, to give some context. I still need to check-in a proposal. |
Beta Was this translation helpful? Give feedback.
-
I think this #100 (comment) should be considered here since both aim to use the same keyword. var x =
from item in collection
where item.foo
select item.bar
do Single();
// or
var x =
from item in collection
where item.foo
do Skip(5)
do ToDictionary(item.Key, item.Value);
// statement form?
from item in collection
where item.foo
do Distinct() into g // `do` the query operator (that proposal)
from item in g
do Console.WriteLine(item); // `do` the terminator (this proposal)
Would it be possible to do that without a query continuation? Edit: from #793 (comment), a "do query termination" in the expression form can be useful as well, private void Test(IEnumerable<int> numbers)
=> from number in numbers do Debug.WriteLine(number); Edit 2: VB uses |
Beta Was this translation helpful? Give feedback.
-
What does Did you mean |
Beta Was this translation helpful? Give feedback.
-
Yeah, |
Beta Was this translation helpful? Give feedback.
-
@alrz's idea sounds great. Here are some more discussions on the subject:
I hope @gafter fixes the first post, he surely referred to interpolated string and forgot to include the quotes. I'd say he should include @alrz's comment in the first post to make the post more productive. |
Beta Was this translation helpful? Give feedback.
-
Hi All,
|
Beta Was this translation helpful? Give feedback.
-
I'm not sure if anyone uses this syntax instead of method chain. I think it isn't really needed feature. |
Beta Was this translation helpful? Give feedback.
-
That argument has already happened. Turns out, despite biases, lots of people use (and even prefer) the query syntax. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour it's very strange... I used to use them until I realized that they just doesn't provide enough agility: no Zip/Aggregate/SelectMany/..., and when you begin to mix them like |
Beta Was this translation helpful? Give feedback.
-
@Pzixel That's why there are so many asks to expand LINQ syntax to cover all those missing use cases. |
Beta Was this translation helpful? Give feedback.
-
@jnm2 but you are still in trouble when you have any custom extension methods over |
Beta Was this translation helpful? Give feedback.
-
@Pzixel Actually, no, there's a proposal for custom extension methods to participate in LINQ syntax too. |
Beta Was this translation helpful? Give feedback.
-
As far as I know, c# compiler handle LINQ keywords magically. Just like this. class Program {
static void Main(string[] args) {
var foo = new Foo() { A = 1, B = "foo" };
var q =
from f in foo // even we can use LINQ keyword for object is not inherited from IEnumerable
where f.A > 0
select f.B;
System.Console.WriteLine(q);
}
}
public class Foo {
public int A { get; set; }
public string B { get; set; }
}
public static class FooExtensions {
public static TResult Select<TResult>(this Foo source, System.Func<Foo, TResult> selector) {
if (source == null || selector == null) return default(TResult);
return selector(source);
}
public static TResult SelectMany<TIntermediate, TResult>(this Foo source, System.Func<Foo, TIntermediate> selector, System.Func<Foo, TIntermediate, TResult> projector) {
if (source == null || selector == null || projector == null) return default(TResult);
var intermediate = selector(source);
if (intermediate == null) return default(TResult);
return projector(source, selector(source));
}
public static Foo Where(this Foo source, System.Func<Foo, bool> predicate) {
if (source == null || predicate == null) return null;
return predicate(source) ? source : null;
}
} The code just show the compiler actual know how to map the LINQ keyword to special extension method. But it only works for built in LINQ keywords, if have a attribute for an extension method, to tell compiler what the keyword is and what type of extension method is(Select/SelectMany/Where/Join/Group...), then compiler can do something like BTW, f# already has a attribute CustomOperationAttribute to extend query expressions's keywords [<AttributeUsage(AttributeTargets.Method, AllowMultiple = false)>]
[<Sealed>]
type
CustomOperationAttribute
=
class
new CustomOperationAttribute : string -> CustomOperationAttribute
member this.AllowIntoPattern : bool with get, set
member this.IsLikeGroupJoin : bool with get, set
member this.IsLikeJoin : bool with get, set
member this.IsLikeZip : bool with get, set
member this.MaintainsVariableSpace : bool with get, set
member this.MaintainsVariableSpaceUsingBind : bool with get, set
member this.Name : string
member this.IsLikeGroupJoin : bool with get, set
member this.IsLikeJoin : bool with get, set
member this.IsLikeZip : bool with get, set
member this.JoinConditionWord : string with get, set
member this.MaintainsVariableSpace : bool with get, set
member this.MaintainsVariableSpaceUsingBind : bool with get, set
end |
Beta Was this translation helpful? Give feedback.
-
@gafter I have almost completed a prototype for #708 and I have been thinking about this one as well. The way I am going about #708 is to synthesize local methods during lowering (LocalRewriter.) Then the other lowering passes will lower the synthesized local methods so that kind of logic is not duplicated. I went for local methods because I wanted to avoid the overhead of closures and they seem more 'correct' in concept, if that makes any sense. Looking at this issue now, the local method approach seems even more like a good idea because local methods can be iterators, i.e. one could write |
Beta Was this translation helpful? Give feedback.
-
I believe I speak in behalf of a large volume of linq lovers when I say |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
Oh yeah, you're right, every possible scenario where linq is used, the programmer actually wants to iterate over every element in a foreach statement. Great observation genius. You prefer a syntax that is not affected by this proposal. |
Beta Was this translation helpful? Give feedback.
-
I disagree with @Pzixel and think his communication style is a bit abrasive, but I don't think we should be discouraging anyone from presenting an opinion on a proposal. |
Beta Was this translation helpful? Give feedback.
-
@scalablecory sorry for inconvenience if any. Well, this position have been explained clear enough so I won't bother you anymore.
You could provide any example when it's not enough... Oh wait, you can't, because there is no any.
Because I'm writing C# and I have to know all C# features in order to be not surprised when I change my project or my job. And duplicating of existing features makes me sad. Eric Lippert has written wonderful articles about duplicating functionallity, especially foreach vs ForEach, which looks pretty similar to this proposal. So if it gets introduces in C# then I have to learn it even if I never use it, because my colleagues may. And I say that I see no benefit here over having a local variable. If you can provide any - then I'd just change my mind. But instead I just get In current form kinda do-monad for LINQ, but pretty limiting and useless. I'd rather like to have full do-monad or HKT in the language, but team priorities are different right now. Being said, I don't want to harsh anybody, so I'm done with this text. You're free contact me in gitter if you wish. I won't violate CoC or anything else, I think I have written enough, if I'm alone thinking that way then who am I to ask changing the course? :) I brought some arguments, if you think they do not deserve to be considered then they don't. |
Beta Was this translation helpful? Give feedback.
-
@scalablecory I'm actually very happy about Pzixel second comment where he said
Which shows how broken the query syntax actually is. I agree with you that we shouldn't discourage people from expressing their opinion. I believe that I have not discouraged Pzixel in any way. If anything he's very much encouraged right now ;) |
Beta Was this translation helpful? Give feedback.
-
@Pzixel var hasEmployees = from employee in context.Employees
where employee.CompanyID == companyID
do Any(); Using foreach var temp = from employee in context.Employees
where employee.CompanyID == companyID
select employee;
bool hasEmployees = false;
foreach(var employee in temp)
{
hasEmployees = true;
break;
} You are right that |
Beta Was this translation helpful? Give feedback.
-
@danielsig I have said something different about foreach, so I'l try to repeat: foreach could replace do-blocks. The only benefit is you have But generally I was talking about introducing temp variable. It's not fair to compare builtin var employees = from employee in context.Employees
where employee.CompanyID == companyID;
select employee;
var hasEmployees = employees.Any(); Or just use method chain syntax var hasEmployees = context.Employees.Where(x => x.CompanyID == companyID).Any(); |
Beta Was this translation helpful? Give feedback.
-
My inner pedant wants to come out and play ...
"wooh, that's so verbose". You can write:
😀 😉 |
Beta Was this translation helpful? Give feedback.
-
@theunrepentantgeek yep, I didn't write it because R# always fix it for me 😄 |
Beta Was this translation helpful? Give feedback.
-
As a small reminder VB.net already has this feature:
https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/queries/aggregate-clause |
Beta Was this translation helpful? Give feedback.
-
Any chance of revisiting this? I find myself avoiding the LINQ query syntax because, very often, I need to wrap the whole query in a And this is a pity because the syntax is so nice to use and so powerful (for example, the |
Beta Was this translation helpful? Give feedback.
-
This issue is relevant to a discussion (#5900) about language-integrated rule definitions, where different kinds of rules could be defined in C# using During the course of the discussion, @HaloFour indicated that syntax like: var rule1 = from c in customers
where c.Name == "John Doe"
do
{
DoSomething1();
DoSomething2();
DoSomething3();
};
var rule2 = from c in customers
from a in accounts
where c.IsPreferred
where a.IsActive
where a.Owner == c
do
{
c.DoSomething1();
c.DoSomething2();
c.DoSomething3();
}; could map so as to utilize As others have expressed in this issue, I'm excited about the possibility of adding With respect to ways that public static IEnumerable<TSource> ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (action == null) throw new ArgumentNullException(nameof(action));
foreach (var item in source)
{
action(item);
yield return item;
}
}
public static IQueryable<TSource> ForEach<TSource>(this IQueryable<TSource> source, Expression<Action<TSource>> action)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (action == null) throw new ArgumentNullException(nameof(action));
return source.Provider.CreateQuery<TSource>(Expression.Call(null, GetMethodInfo(Queryable.ForEach, source, action), new Expression[] { source.Expression, Expression.Quote(action) }));
} |
Beta Was this translation helpful? Give feedback.
-
If possible, I'd like to see not only var items = (from ... in ...
where ...
let... = (from ... in ...
form ... in ...
where ...
select ...).Average() // <--
where ...
group ... by ... into ...
let... = (from ... in ...
where ...
select ...).Sum() // <--
orderby ... descending
select new
{
...
}).Take(7).ToArray(); // <-- Where some expressions might be I really love the LINQ query syntax, as it is immediately obvious what exactly it does -- as opposed to the fluent-style method-chaining syntax equivalent (Yes, I know that you can express nested LINQ queries using helper functions blah blah blah, but it is far less obvious to a reader what exactly you are doing). However, having to wrap everything in parenthesises breaks the reader's flow and overall readability. I'd therefore much rather have the LDT to generalize this proposal and add a language construct to expand the current set of LINQ query contextual keywords. I imagine something of the following (only spitballing): [LINQExtension(keyword = "every", extensionType = LINQExtensionType.QueryBodyClause)]
public static IEnumerable<T> EveryNth<T>(IEnumerable<T> collection, int stride) =>
collection.Where((_, i) => (i % stride) == 0);
[LINQExtension(keyword = "median", extensionType = LINQExtensionType.QueryTerminator)]
public static T Median<T>(IEnumerable<T> collection)
where T : IComparable<T>
{
if (collection.ToList() is { Count: > 0 } list)
{
list.Sort();
return list[(int)(list.Count * .5)];
}
else
throw new InvalidOperationException("collection is empty");
}
[LINQExtension(keyword = "zipwith", extensionType = LINQExtensionType.QueryBodyClause)]
public static IEnumerable<(T, U)> Zip<T, U>(IEnumerable<T> collection, IEnumerable<U> other)
{
using var t = first.GetEnumerator();
using var u = second.GetEnumerator();
while (t.MoveNext() && u.MoveNext())
yield return (t.Current, u.Current);
}
[LINQExtension(keyword = "do", extensionType = LINQExtensionType.QueryTerminator)]
public static void Do<T>(IEnumerable<T> collection, Action<T> action)
{
foreach (T item in collection)
action(item);
} Which could be used as follows: var collection = ....;
var med = from x in collection
every 3
median;
from x in collection
zipwith my_other_collection
do x =>
{
....
}; My concept proposes that LINQ-contextual keywords may be registered using some kind of attribute, which also provides some information about the keyword itself (whether it is a LINQ query terminator, a LINQ query body clause, etc.). Whether the "custom" LINQ keyword accepts an additional parameter should be determined by the method signature itself. However, for a custom LINQ-contextual keyword to be used, the method must fulfil the following requirements:
I understand that implementing such a system is a huge feat, however, one could restrict this feature to the .NET runtime at first, and then open the usage of |
Beta Was this translation helpful? Give feedback.
-
@Unknown6656 |
Beta Was this translation helpful? Give feedback.
-
This will be really incomplete if async operations are not included. For example when working with data from database and one needs to call |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
This is for a new statement form something like
For example
See also dotnet/roslyn#1938
Beta Was this translation helpful? Give feedback.
All reactions