-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Proposal] Syntactic ranges #7632
Comments
Without commenting yet on any other parts of your proposal, I object to the use of |
The form with lambda expression is a no. "Increment operation" seems silly. it should use a value for custom steps, just like F# or any other language. Also it would be nice if it would capable of representing infinite ranges. |
@alrz I agree. |
@orthoxerox I thought a lot about this, and I do agree with you, but I used this simply because in my opinion it's the most useful behavior without complicating syntax. |
I believe #6949 should use target type to return desired type of collection. Same is applicable here. int[] arr = [ 0 .. 10 ];
List<int> list = [ 0 .. 10 ];
IEnumerable<int> infinite = [ 0 .. ]; |
Why not |
@GeirGrusom I don't know what translations you have in mind for ranges. (1) Having all of them translated to F# fills arrays for both |
@GeirGrusom I think having On the other hand, public static IEnumerable<int> r(int from, int to) {
for (var i = from, i <= to, i++)
yield return i;
} allows you to write |
@orthoxerox You love this one don't you? 😉 |
(1) It's not, and that wasn't the point. It's to make a better substitution for Enumerable.Range, because Enumerable.Range is both a long expression to write, and there are tons of things it can't express. (3) You are making an assumption on what the compiler will do. However you are treading into something I think is outside of this proposal's scope. @orthoxerox yes I agree that the square brackets may come off as a little bit misleading. However the generator has the issue of creating a state machine in every use-case, and you cannot generalize it as you mentioned. I think ranges is a common enough use case that it warrants syntax. The C# compiler can generate a normal for-loop for common ranges, but using your generators it will never do so. |
oohhhkay. |
Fine! Jeez! edit: but any performance benefits I would claim is imaginary. |
What do you think about the range issue? Should it simply be inclusive, or should there be a way to show that the range obviously is exclusive? |
@alrz What is really the benefit of having an implicit conversion from range to arrays and collections? |
@GeirGrusom You will be surprised by the implementation of How do you propose these ranges work on doubles, btw? |
Yes I know. It's up to the compiler to actually use it as a generator though. It probably does, but it doesn't have to. Same with ranges. The compiler can absolutely generate generators, but it doesn't have to. It depends on context. For doubles (or any type really) the default behavior should be incremented by 1 implicitly (should probably allow explicit as well) cast to the expression type. This is the rule for any type, not just integers. Any issue with precision is in the hands of the developer like in every other case. |
@GeirGrusom You cannot assume anything about how this feature will be used. In F# you can explicitly specify the desired type: let arr = [| 0 .. 10 |]
let list = [ 0 .. 10 ]
let seq = seq { 0 .. 10 } As well for clusivity. You shouldn't limit a feature to just one possibility, otherwise it woudn't be usable that much. These are not "out of scope of this proposal". I should ask, what is the benefit of having ranges as a language feature in the first place? Yes, clarity. So if ranges are meant to be used instead of any other mechanism they should provide best performance and not be bound to a specific type. See #6949 for more on this.
It's not up to the compiler to do these optimizations. Just like #7580. |
You should check the IL generated by those expressions, because the F# creates .ToArray() and .ToList() on the iterators. So edit: which is really obvious. How else should it be represented in IL?
How did I limit anything? I just proposed a range syntax.
There is really nothing in .ToList() or .ToArray() that indicates poor performance. It's a hint for the compiler (the JIT or AOT compiler).
For Roslyn? Perhaps not, but for JIT and AOT compilers it certainly is. It may be smart to define a concrete type that is returned by ranges though, but I would rather let compiler developers think about performance impact than me as a customer. They are much more competent in the matter. |
@GeirGrusom Yes, you're right F# doesn't do that. I'm thinking that it can fill an array, by performance hit I meant iterating an |
I think brackets are not necessary and also it can be a pattern, e.g. foreach(var i in 1..10) {}
var integer = 5;
switch(integer) {
case 1..10: break;
} If we don't use a generic expression (which I believe is ambiguous), inclusive ranges would be just a minus away, var n = 10;
var i = 1..n; // 1 to 10
var j = 1..n - 1; // 1 to 9 Besides of stepped ranges |
@alrz shouldn't |
@orthoxerox Actually it should have lower precedence (then |
I'm failing to see the value of this over a library/libraries. |
@paulomorgado If you ask me, anything more than earlier versions of C# could be done with a library or a bunch of boilerplate code, I think the point is to avoid just that, otherwise, none of these are really necessary. (don't take this personal but the whole value of #3571 is to avoid a pair of parentheses, right?) |
@alrz you're right, I always confuse the two meanings. |
I don't see The minimal value of #3571 is a pair of parenthesis and a do,.but can be more. It was just my take on the many proposals for something like that. But, in the end, it's not a way to replace libraries but a way to leverage libraries. |
@paulomorgado To support your point, I can tell that range patterns also can be implemented via class Between { public static bool operator is(this int self, int from, int to) => self <= from && self >= to; }
if(integer is Between(1, 10)) {}
integer switch(case Between(1, 10): ...)
// etc But given that this is really a basic pattern to follow I think language should make it easier to do, nevertheless, I can live without it. 😄 |
More expressive. To the point.
If you need anything other than |
Give that the libraries would have to exist, what would that boilerplate code would be? You can't justify new language features that will require libraries with the fact that those libraries don't exist now. |
Imagine if Nullable wasn't a language feature. Imagine that you couldn't create a generic nullable container (because unlike nullable, ranges cannot be generic). Would you advocate that they should keep nullable out of the language and rather that everyone implemented NullableInt, NullableBool, NullableVector4 etc. everywhere? It's not boilerplate if there's a library for it you claim.
No one is going to write a library to create ranges for every imaginable type. System.Numerics.Vectors.Vector4 for example doesn't have a Range implementation. You would have to either find someone who has implemented it, which is a waste of time because 1) it doesn't take much code to implement Range for it and 2) No one is going to be running around implementing a range library simply for Vector ranges. However having a range syntax spares you from doing this work at all, and the result very clearly states the intention of the code in a format that everyone understands. |
@alrz brackets may not be necessary. Also perhaps increment could be specified using var range = 0 .. 10 += 2; |
@GeirGrusom It will be ambiguous, it can be parsed like |
|
you mean like built-in operators for ranges? See, |
Delegate's += swaps out the delegate. How is this any different? |
The idea is that += operator applied on a range type results in a new range type with a different step. It doesn't change it, it creates a new type. IEnumerable shouldn't be returned by a range. It should return some type that implements IEnumerable. |
So actually, when I need a stepped range I have to allocate two and abandon the first one somehow? Then what is the point of ranges at all! |
What? Where do you get that from? It's not like 1 + 2 + 3 + 4 * 3 actually does all those calculations in runtime, so why on earth does the ranges have to? |
@GeirGrusom, You are using |
It would support NodaTime or Vector4, or any type that supports increment without someone writing a library or boilerplate for it. It also clearly indicates what the expression does. It also means that the expression makes sense to the compiler such that a |
I was making my own extension method
Should be just this Any other transform could be made from math on Linq
|
See dotnet/csharplang#185 for a feature planned for C# 8. Future language discussion should be directed to https://github.com/dotnet/csharplang . |
I suggest a feature in C# to easily define ranges. It's a replacement for Enumerage.Range except it defines start and stop points rather than start and count.
Sometime you wish to create a range to perhaps .Select from. I propose the following syntax:
The requirements of the type from
expression
is that both have to be of the same type. You cannot create a range between float and integer for example. The result set is [a → b].edit: changed to inclusive. F# is inclusive, so why should C# be different?
The type of
expression
also must support an appropriate incremental operator, prefix ++, postfix++ or += (int), or += T if expression is implicitly convertible from int, in that order.If the order is reversed (high to low) the result of the expression is an empty set.
Example:
An extension could be that it allowed a third argument:
This allows the developer to specify an increment step. The type of the argument must be implicitly convertible to T.
edit: removed left hand type inference.
The text was updated successfully, but these errors were encountered: