csharplang/meetings/2018/LDM-2018-01-22.md
2018-03-20 17:38:03 -07:00

3.4 KiB

C# Language Design Notes for Jan 22, 2018

Agenda

We discussed the range operator in C# and the underlying types for it.

  1. Inclusive or exclusive?
  2. Natural type of range expressions
  3. Start/length notation

Inclusive or exclusive?

var numbers = ints[from..to];

Should this mean inclusive or exclusive of the element at to?

Not many languages have only inclusive ranges. Apart from F#, they tend to either have exclusive ranges or have notations for both (at the upper end; the lower is always inclusive).

Python uses exclusive ranges. A lot of people seem to be confused about it; it doesn't immediately gel with their intuition. The obvious advantage of course is that the collection's Length is directly allowed at the end:

var s = a[0..a.Length];

In foreach loops over ranges, if you use constant end points, again intuition seems to suggest inclusiveness:

foreach (var x in 1..100) { ... }

foreach (var x in 0..100) { ... }

You don't often use constants in practice, though, except as a zero lower bound. In fact, in F# it is often a bit of a pain to write a for loop for array iteration, for instance, because you need to subtract one at the end to avoid overrunning.

Let's look at these four scenarios:

  1. Create a range, incl, that goes from start through end
  2. Create a range, excl, that goes from start up to end (but not including)
  3. Create a range, rel, that goes from start and has length elements
  4. Create an empty range, emp, that starts at 0.

This is what they will look like, given exclusive semantics, inclusive semantics, and a notation for start:length ranges:

// Exclusive x..y
Range incl = start..end+1;
Range excl = start..end;
Range rel  = start..start+length;
Range emp  = 0..0;

// Inclusive x..y
Range incl = start..end;
Range excl = start..end-1;
Range rel  = start..start+length-1;
Range emp  = 0..-1;

// Relative x:l
Range incl = start:end-start+1;
Range excl = start:end-start;
Range rel  = start:length;
Range emp  = 0:0;

The one that seems to have the least amount of computational gymnastics across the scenarios is the exclusive option. Also, the inclusive notation for an empty range is severely unappetizing! It is of course entirely possible that we have more than one syntax.

One idea is that when we start talking about scenarios outside of indexing, we simply have a different syntax; maybe one that is built in to language constructs for containment and iteration:

bool b = x is in 3 to 5;
foreach (var x in 0 to 100) { ... }

Conclusion

Let us go with .. means exclusive. Since we've chosen to focus on the indexing/slicing scenario, this seems the right thing to do:

  • It allows a.Length as an endpoint without adding/subtracting 1.
  • It lets the end of one range be the beginning of the next without overlap
  • It avoids ugly empty ranges of the form x..x-1

Natural type of range expressions

If we allow Range to be the natural type of range expressions, the following will work:

var r = 4..6; // infer Range

On the other hand, if we do this, Range will be forever tied to range expressions as the preferential type.

Conclusion

We're good with that.

Start/length notation

We may want an additional notation for specifying start and length. It can technically be added later, but if we want to do it we should try to do it now.