dotnet / csharplang

The official repo for the design of the C# programming language
11.48k stars 1.02k forks source link

C# 8 - System.Index #2148

Closed Korporal closed 5 years ago

Korporal commented 5 years ago

I'm playing with VS 2019 and building a project with C# 8, however the use of ^is hampered because the compiler cant find System.Index. I have no idea where that is and it doesn't seem to be a nuget package. So am I able to use ^in VS 2019 yet or not?

YairHalberstadt commented 5 years ago

Is the project .net core, .net standard or .net framework?

akarpov89 commented 5 years ago

@Korporal You should target .NET Core 3.0 in order to make System.Index and System.Range available.

jnm2 commented 5 years ago

@Korporal If you don't want to target notecoreapp3.0, another option is to copy and implement the API surface area of System.Index and System.Range into your project. For library projects, this is only a good idea if they stay internal. Otherwise you will have conflicts down the road when a project uses your library but already has these types defined in another library.

popcatalin81 commented 5 years ago

Why isn't this a .Net standard package instead of .Net Core one?

jnm2 commented 5 years ago

@popcatalin81 It is being added to .NET Standard 2.1. https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/

Platform dependencies

Most of the C# 8.0 language features will run on any version of .NET. However, a few of them have platform dependencies.

Async streams, indexers and ranges all rely on new framework types that will be part of .NET Standard 2.1. As Immo describes in his post Announcing .NET Standard 2.1, .NET Core 3.0 as well as Xamarin, Unity and Mono will all implement .NET Standard 2.1, but .NET Framework 4.8 will not. This means that the types required to use these features won’t be available when you target C# 8.0 to .NET Framework 4.8.

From memory, which is more or less accurate, but I don't have references:

.NET Standard packages like System.ValueTuple conflict with adding the types in-box for .NET Core 3.0. Moving types in-box has caused no end of tooling bugs and headaches for the team. They want it in-box for C# 3.0 because they don't want to add an entire separate package just to hold these couple of types.

akarpov89 commented 5 years ago

I asked @terrajobst if they plan to create a nuget package exposing System.Index and System.Range for target frameworks other than .NET Core 3.0 a while ago on twitter (https://twitter.com/akarpov89/status/1074804447627395072) and he gave me a good explanation:

To avoid the disaster that was System.ValueTuple we'll first ship .NET Core 3.0 / .NET Standard 2.1 that offer built-in support and then we'll figure out the down-level story. In general, providing a package for a platform-level exchange type is hard & fragile. Also, I'm not sure they are worth it as we cannot provide support for indexers on strings and arrays that would take Index/Range as those requrie updating the platform, so there would be a significant loss in fidelity. Plus, shipping out-of-band by definition requires a new assembly which for core types like these seems very undesirable as we already have too many. So my goal would be to say no down level, but we'l; say. Engineering reality is rarely pretty :-)

Korporal commented 5 years ago

Well I had little time to explore yet, but I just created a 2019 console app (seems to target .Net Core 2.1 by default and no other .Net Core version seems to be present). Anyway I found (on nuget) a package named

sdcb.System.Range although this doesn't seem to be Microsoft.

Korporal commented 5 years ago

I do think that the syntax that was settled on for this is poor and the lack of symmetry when accessing the "first" element and "last" element is horrible. I wonder if these would have been better:

var x = array[<3]; // rather than array[^3] - i.e. we're perceiving the array from right-to-left

then add support for this (while still supporting [3])

var x = array[>3]; // a way to write array[3] - i.e. we're perceiving the array from left-to-right

along with 0 always referring to lowest indexed element and highest indexed element depending on whether <(or, sadly, in reality ^) is used. This would enable developers to write code that uses both "directions" in a clearer manner if they so chose.

There may be good reasons why the team did it they way they did, but the lack of symmetry makes no sense to me and will no doubt lead to subtle bugs where code is using both kinds of subscripts (perhaps calculating those subscripts) to fiddle with arrays and the developer simply forgot that 0 is first and ^1 is last!

As I said elsewhere the index/subscript could have been defined as either the +ve offset from the "first" element or -ve offset from the "last" element. As it stands now ^X means "negative offset X from last element + 1".

Is it too late to change this?

HaloFour commented 5 years ago

but the lack of symmetry makes no sense to me

When you start with an incorrect assumption any conclusion will be unreliable.

will no doubt lead to subtle bugs where code is using both kinds of subscripts (perhaps calculating those subscripts) to fiddle with arrays and the developer simply forgot that 0 is first and ^1 is last!

Those same developers also had to learn that array[array.Length] didn't point to an element.

Is it too late to change this?

It's not a question of time, it's a question of justification. One person vehemently disliking something on the Internet is far from a reason for the team to reverse design decisions that came out of numerous design meetings and prototypes.

Korporal commented 5 years ago

but the lack of symmetry makes no sense to me

When you start with an incorrect assumption any conclusion will be unreliable.

will no doubt lead to subtle bugs where code is using both kinds of subscripts (perhaps calculating those subscripts) to fiddle with arrays and the developer simply forgot that 0 is first and ^1 is last!

Those same developers also had to learn that array[array.Length] didn't point to an element.

Is it too late to change this?

It's not a question of time, it's a question of justification. One person vehemently disliking something on the Internet is far from a reason for the team to reverse design decisions that came out of numerous design meetings and prototypes.

@HaloFour - Understood. But can anyone explain what advantages the chosen approach has over my suggested one? I'd truly like to understand the reasoning here.

Thanks.

YairHalberstadt commented 5 years ago

But can anyone explain what advantages the chosen approach has over my suggested one? I'd truly like to understand the reasoning here.

Complexity. You are suggesting adding two syntaxes, both of which people have to learn, and both of which would be easy to confuse, instead of just one. Instead of just remembering that ^1 points to the last element, they've got to remember which one of >1 and <1 points to the last element.

Korporal commented 5 years ago

But can anyone explain what advantages the chosen approach has over my suggested one? I'd truly like to understand the reasoning here.

Complexity. You are suggesting adding two syntaxes, both of which people have to learn, and both of which would be easy to confuse, instead of just one. Instead of just remembering that ^1 points to the last element, they've got to remember which one of >1 and <1 points to the last element.

But can anyone explain what advantages the chosen approach has over my suggested one? I'd truly like to understand the reasoning here.

Complexity. You are suggesting adding two syntaxes, both of which people have to learn, and both of which would be easy to confuse, instead of just one. Instead of just remembering that ^1 points to the last element, they've got to remember which one of >1 and <1 points to the last element.

@YairHalberstadt

Except that >1 would (or [1]) point to the second element - see even you're confused!

I find it hard to accept that the tokens GT or LT when found within [ GT | {LT} <expression> ] is to be regarded as "complex". In reality the usage and need to remember that 0 is first and ^1 is last is more complex for the reader and writer of the language - it's that complexity that needs to be considered I would argue. You should ask yourself how would someone who knew C# interpret code they see for the first time that's using ^ - I'm confident that most readers of such code would naturally assume ^0 meant last element - did you ask around?

Just watch, once this is full released and available to all for production code you're going to see articles and blogs decrying this design.

HaloFour commented 5 years ago

@Korporal

Understood. But can anyone explain what advantages the chosen approach has over my suggested one? I'd truly like to understand the reasoning here.

The onus would be on you to demonstrate why your suggestion is considerably better.

I'd argue that < is worse because it implies that it affects the clusivity of a given range, not the position of the index. Ranges are always end exclusive. Beyond that I don't see why < is any better than ^.

Except that >1 would (or [1]) point to the second element - see even you're confused!

1 already points to the position before the second element. There's no need for any new syntax there.

Korporal commented 5 years ago

@HaloFour

1 already points to the position before the second element. There's no need for any new syntax there.

I'm not arguing that <is needed but that it naturally arises from the choice to introduce a new operator for reverse sub-scripting. Once you decide that arrays can now be accessed bidirectionally then that decision leads to the suggestion for supporting two operators as an improvedway of expressing code that relies on directionality.

Prior to ^ a subscript was a subscript value with one meaning but as soon as you introduce ^ you change that and so it seems natural to me to reflect that symmetry linguistically and allow (optional) use of >.

Korporal commented 5 years ago

Anyway I guess nobody's in a position to review the decision, its cast in stone - so be it!

HaloFour commented 5 years ago

@Korporal

Just watch, once this is full released and available to all for production code you're going to see articles and blogs decrying this design.

This is true of literally every language decision made by the C# team. If they didn't design features out of fear that someone somewhere would disagree loudly they'd literally never design anything.

it seems natural to me to reflect that symmetry linguistically

The symmetry of ^ has been explained multiple times.

Anyway I guess nobody's in a position to review the decision, its cast in stone - so be it!

It's certainly up for review, nothing is cast in stone until the feature ships. The ^ operator was chosen as a strawman, but you'd have to demonstrate why < or anything else is better than it, enough that someone on the language design team is willing to make the argument. I'm not saying that won't happen. Clusivity issue aside I don't find it any worse than ^, but that's not necessarily a reason to consider it. But, in my opinion, if you argue that < and > should be added in tandem as indexing operators you'd probably have a much steeper hill to climb.

BreyerW commented 5 years ago

To me current symmetry is logical rather than mathematical . The thing is, accessing array is mostly based on math not logic. Due to this i foresee that subtracting or adding by one will be quite common due to this discrepancy while one of the minor motivations for this feature is to eliminate such bothersome quirks in as much places as humanly possible. E. g. imagine substracting equal amount of elements from start and end. With current implementation you MUST add by one on end and only end otherwise at best u get out of bounds exception (when substracting 0 elements) and at worst subtle bugs because end is off by one (when substracting more than 0 elements)

HaloFour commented 5 years ago

@BreyerW

The primary motivation is to support slicing, in which case the math works pretty well:

var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var slice = array[2..^2]; // { 3, 4, 5, 6, 7 }

And that's exactly how this works with languages that use negative indexing:

>>> list = (1, 2, 3, 4, 5, 6, 7, 8, 9)
>>> list[2:-2]
(3, 4, 5, 6, 7)
CyrusNajmabadi commented 5 years ago

As I said elsewhere the index/subscript could have been defined

Yes. That is understood. As mentioned several times already, these alternatives were considered, debated, and decided upon based on a wealth of information and cases that demonstrated to the LDM that the current approach is preferable. This was also done with a strong look at the ecosystem out there and how this has worked out elsewhere.

Also, as noted several times already: nothing about this design is preventing complimentary indexing/ranging proposals from happening in the future. We picked this design knowing that it would be great for many cases, and intuitive for many, but also not great for some cases. That was going to happen as long as we only allowed a single indexing/ranging approach to start with.

The intent here is to have support for the most common and most valuable style, and then see in hte future if it's necessary to provide direct language support for hte less valuable style.

--

If you don't want to wait, you can easily add your own .Gleave extension method in the meantime that gives you the semantics you want.

CyrusNajmabadi commented 5 years ago

@HaloFour - Understood. But can anyone explain what advantages the chosen approach has over my suggested one? I'd truly like to understand the reasoning here.

To me, the current approach is far more intuitive and consistent. It also fits with so many other languages out there. So it's much more advantageous to go with the current POR.

xCyborg commented 5 years ago

I do think that the syntax that was settled on for this is poor and the lack of symmetry when accessing the "first" element and "last" element is horrible. I wonder if these would have been better:

var x = array[<3]; // rather than array[^3] - i.e. we're perceiving the array from right-to-left

then add support for this (while still supporting [3])

var x = array[>3]; // a way to write array[3] - i.e. we're perceiving the array from left-to-right

along with 0 always referring to lowest indexed element and highest indexed element depending on whether <(or, sadly, in reality ^) is used. This would enable developers to write code that uses both "directions" in a clearer manner if they so chose.

There may be good reasons why the team did it they way they did, but the lack of symmetry makes no sense to me and will no doubt lead to subtle bugs where code is using both kinds of subscripts (perhaps calculating those subscripts) to fiddle with arrays and the developer simply forgot that 0 is first and ^1 is last!

As I said elsewhere the index/subscript could have been defined as either the +ve offset from the "first" element or -ve offset from the "last" element. As it stands now ^X means "negative offset X from last element + 1".

Is it too late to change this?

I totally agree, besides : there is inconsistency between hat Index value in isolated vs Range context.

CyrusNajmabadi commented 5 years ago

I totally agree, besides : there is inconsistency between hat Index value in isolated vs Range context.

how so? in both cases they should be making a System.Index instance in the same manner.

xCyborg commented 5 years ago

I totally agree, besides : there is inconsistency between hat Index value in isolated vs Range context.

how so? in both cases they should be making a System.Index instance in the same manner.

Well:

Index i1 = 3; Index i2 = ^2; int[] list = {0, 1, 2, 3, 4, 5, 6, 7, 8}; var val1 = list[i1]; // equals 3 var val2 = list[i2]; // equals 7 var slice = list[i1..i2]; // equals { 3, 4, 5, 6 } NO 7!

HaloFour commented 5 years ago

@xCyborg

This has already been explained. Indexes refer to the position between the elements and don't carry an implied directionality. So when you use an index as the end of a range it is exclusive of the next element after that index.

This behavior is consistent with other languages that support ranges and slicing with negative indexes:

>>> list = (0, 1, 2, 3, 4, 5, 6, 7, 8)
>>> list[-2]
7
>>> list[3:-2]
(3, 4, 5, 6)
CyrusNajmabadi commented 5 years ago

@xCyborg There's no inconsistency there. In both cases i2 is the System.Index(2, fromEnd) value. For ranges, the right side is exclusive, and hte left side is inclusive. For a normal index, it's as if you're only supplying the left side and saying you want a length of 1. So this is consistent here.

Korporal commented 5 years ago

@HaloFour

I fail to see the significance of "This behavior is consistent with other languages", sure these can serve as a basis, an impetus but C# is it's own language and striving to make it adopt or mimic other languages when there are potential improvements to be had by deviating from them strikes me as a backward thinking.

The disregard for symmetry is a mistake in my opinion and the coupling of inclusivity/exclusivity with the way "other languages do it" is not innovative.

I really believe that this is going to become seen as a badly implemented capability in years to come, please reconsider, clear the whiteboard and start again.

HaloFour commented 5 years ago

@Korporal

I fail to see the significance of "This behavior is consistent with other languages", sure these can serve as a basis, an impetus but C# is it's own language and striving to make it adopt or mimic other languages when there are potential improvements to be had by deviating from them strikes me as a backward thinking.

Other languages serve as the precedent for how any given feature may be designed. When that design is repeated many times over by completely different teams for completely different languages it speaks to the validity of that design. This model fits well within C#, with the exception that C# cannot flip to negative indexing due to compatibility constraints. It doesn't remotely matter what operator is decided upon here, even if the team decided to drop ^ for something else, this is still how it will behave because this is how it makes the most sense to behave.

The disregard for symmetry is a mistake in my opinion and the coupling of inclusivity/exclusivity with the way "other languages do it" is not innovative.

This design does not disregard symmetry. The symmetry has been explained countless times already. You have this model of how indexing and slicing works in your head which is not congruent with reality.

I really believe that this is going to become seen as a badly implemented capability in years to come, please reconsider, clear the whiteboard and start again.

You're welcome to your opinion, but reality seems to disagree with you. You not liking something does not make it bad design.

CyrusNajmabadi commented 5 years ago

I fail to see the significance of "This behavior is consistent with other languages", sure these can serve as a basis, an impetus but C# is it's own language and striving to make it adopt or mimic other languages when there are potential improvements to be had by deviating from them strikes me as a backward thinking.

The current design is felt to be the improved design. This was based on several investigations into the pros/cons of inclusive vs exclusive ranges. In practice, exclusive worked out far nicer and cleaner.

As i've also mentioned, nothing about hte current design precludes adding inclusive ranges in the future. however, that assessment will be done at a later point to see if it's really necessary or not. The belief is that exclusive is best to have first, and inclusive can be added later if appropriate.

is not innovative.

Being 'innovative' is a non-goal.

I really believe that this is going to become seen as a badly implemented capability in years to come, please reconsider, clear the whiteboard and start again.

The alternatives came with more problems in more cases. The cases have already been enumerated and assessed. No new data has appeared to change any thinking here.

CyrusNajmabadi commented 5 years ago

The disregard for symmetry i

What disregard for symmetry? inclusive-start/exclusive-end ends up one of the most symmetrical approaches. I think there's an issue somewhere where this is all discussed. Maybe someone can link to it (or you can search for it and read through)?

Korporal commented 5 years ago

Here are the basic concepts involved:

Devising a symmetrical notation this is a key step, forget "other languages".

int array[] = {A,B,C,D,E,F,G,H,I};

// Directionality:


var X = array[->2]; // X becomes C, backward compatibility means that [->n] is the same as [n].
var X = array[2<-]; // X becomes G.

var X = array[2->]; // X becomes D.
var X = array[<-2]; // X becomes F.

// Inclusivity/Exclusivity

var inclusive_slice = array[->2..2<-]; // yields: C, D, E, F and G.

var exclusive_slice = array[2->..<-2]; // yields: D,E and F.

The suggested operators -> and <- denote directionality and their position relative to their operands denotes inclusivity and exclusivity.

We can then (intuitively) write:

// element 2 from the left including that element - through to - element 3 from the right excluding that element.

var some_slice = array[->2..<-3]; // yields: C, D and E.
HaloFour commented 5 years ago

@Korporal

That's much more complicated for very little reason and requires a whole bunch of extra glyphs to handle the 99% use case. What's intuitive to you is a big mess to everyone else. Not to mention, -> is already an operator in C#.

You act as if the team hasn't considered all of the issues with clusivity and that they're just copying Python (and countless other languages). That is not the case. They've already discussed this.

Korporal commented 5 years ago

@Korporal

That's much more complicated for very little reason and requires a whole bunch of extra glyphs to handle the 99% use case. What's intuitive to you is a big mess to everyone else. Not to mention, -> is already an operator in C#.

It is not "more complicated" it simply provides a means to independently represent directionality and inclusivity, two quite different concepts. The notation (just a suggestion) makes it easy to represent any combination of these.

You act as if the team hasn't considered all of the issues with clusivity and that they're just copying Python (and countless other languages). That is not the case. They've already discussed this.

I disagree, I simply think the way your doing this is fundamentally flawed.

HaloFour commented 5 years ago

@Korporal

It is not "more complicated"

It is definitely more complicated. It conflates points with directions. Indexes don't have a directionality, not in C# and not in any other language. It requires a combination of disparate glyphs simply to handle the most common use case with slicing.

I disagree, I simply think the way your doing this is fundamentally flawed.

Then there's nothing left to talk about. You're in the minority here, where it regards C# and where it regards computer science as a whole. Decades of experience with indexing, ranges and slices aren't going to be thrown away because someone doesn't approve of it on the Internet.

CyrusNajmabadi commented 5 years ago

@Korporal Alternative approaches were considered and rejected due to complexity. Your feedback has been heard and is definitely something that was taken into account.

I disagree, I simply think the way your doing this is fundamentally flawed.

Understood. The LDM currently disagrees. Thanks for the feedback!

Korporal commented 5 years ago

@Korporal

It is not "more complicated"

It is definitely more complicated. It conflates points with directions. Indexes don't have a directionality, not in C# and not in any other language.

My suggested notation was just that, suggested in order to demonstrate a point here in this discussion. I never said "indexes have a direction" did I? No, I did say that the problem you are striving to solve involves two concepts, directionality and exclusivity which is I think an accurate statement.

It requires a combination of disparate glyphs simply to handle the most common use case with slicing.

It involves a total of three tokens Halo, one being ... the others being -> and <-. These tokens form a simple alphabet that elegantly and symmetrically allows you to express any possible combination of implied direction and exclusivity that the teams proposed and frankly ugly notation is able to express.

I disagree, I simply think the way your doing this is fundamentally flawed.

Then there's nothing left to talk about. You're in the minority here, where it regards C# and where it regards computer science as a whole. Decades of experience with indexing, ranges and slices aren't going to be thrown away because someone doesn't approve of it on the Internet.

Please stick to the subject Halo, we're discussing the proposed implementation and notation for ranges and indexes and I'm simply disagreeing with the chosen approach. If you are unable to discuss this in a civil and objective way without making personal remarks about my perceived capabilities you should refrain from participation.

Finally I'm quite aware that -> is an existing token in the C# language, yet so is a period . which also enjoys several distinct use cases (decimal number: 123.45, qualification: some_object.some_property) I'm sure the grammar could easily be adapted to allow my suggested use or choose some other token, all I wanted to convey was symmetry with some way of also expressing directionality.

HaloFour commented 5 years ago

@Korporal

No, I did say that the problem you are striving to solve involves two concepts, directionality and exclusivity which is I think an accurate statement.

Ranges, yes. Indexes, no. Indexes don't have either directionality or clusivity.

As for clusivity, the team has decided to tackle the 99% use case with ranges today, optimized for slicing.

It involves a total of three tokens Halo

Two more than necessary, especially for the 99% case.

Please stick to the subject Halo

Which subject is that? That the current proposal is an asymmetric clone of other languages without thought or innovation? I'm responding to the constant repetition of refuted claims. If we can't agree on the basic premise on which the design is founded then there isn't much conversation to be had.

Korporal commented 5 years ago

@HaloFour - Very well Halo, I'm aware this subject is decided and there's no possibility of change. But just for the record do you think there is any scenario or examples that the suggested notation is unable to represent?

I stand by what I said that a symmetrical and intuitive notation for this will lead to more readable code, I bet you any example you care to cite of the chosen Microsoft notation can be equally and more readably represented by the symmetrical notation (the notation for which was merely a suggestion for the sake of discussion - you could use |> and <| for example,).

HaloFour commented 5 years ago

@Korporal

I stand by what I said that a symmetrical and intuitive notation for this will lead to more readable code,

I agree, good thing that the LDM chose a symmetrical and intuitive notation, backed by decades of precedent.

I bet you any example you care to cite of the chosen Microsoft notation can be equally and more readably represented by the symmetrical notation

I think that foo[0..^1] is much more recognizable and understandable than foo[|>0..1<|]. That looks like a funky emoji or something rejected from Perl.

Korporal commented 5 years ago

@Korporal

I stand by what I said that a symmetrical and intuitive notation for this will lead to more readable code,

I agree, good thing that the LDM chose a symmetrical and intuitive notation, backed by decades of precedent.

I bet you any example you care to cite of the chosen Microsoft notation can be equally and more readably represented by the symmetrical notation

I think that foo[0..^1] is much more recognizable and understandable than foo[|>0..1<|]. That looks like a funky emoji or something rejected from Perl.

Except it would in fact be:

foo[|>0..0<|]

Which is certainly more elegant than the MS notation.

See also this post.

HaloFour commented 5 years ago

@Korporal

Which is certainly more elegant than the MS notation.

Elegance is a question of opinion. I don't think that your syntax is more elegant. I think it's more noisy, and unnecessarily so. And it also indexes incorrectly which means that most people would get it wrong, mainly because it's not symmetrical nor intuitive.

Korporal commented 5 years ago

@HaloFour - As you wish, I think I've said all I wanted to.

Korporal commented 5 years ago

Oh by the way is there any official documentation yet in exactly how these new C# 8 features work? All I can find is the odd blog post but nothing formal and official.

CyrusNajmabadi commented 5 years ago

Proposal is here: https://github.com/dotnet/csharplang/blob/master/proposals/ranges.md

mqudsi commented 5 years ago

Back on topic, can Roslyn emit a more useful error when trying to use ranges on .NET Core 2.2 with <LangVersion>preview</LangVersion> already set?

vadimkantorov commented 5 years ago

Can Index be passed to methods similar to Python's F.softmax(x, dim = -1)? Passing these sorts of negative indices is very common in deep learning libraries (like PyTorch) to refer to tensor dimensions counted from backwards. The methods like this would very frequently pass it to other methods or sometimes do arithmetic on the dimension (along with some tensor reshaping) and passed the modified dimension to other methods. I am sure this usecase was very likely discussed, just wanted to highlight that it's really common. @jcouv

jcouv commented 5 years ago

@vadimkantorov Yes, you can definitely pass Index values as arguments. Declare void M(Index i) ... and invoke it with M(1) or M(^1) (from end).

There is one caveat though: in C# you cannot set a parameter's default value to be ^1. So declaring void M(Index i = ^1) is disallowed (^1 is not a constant). I'm not sure if it's feasible to allow constants of Index type, or what would be involved.

yaakov-h commented 5 years ago

@jcouv #688? 😄

vadimkantorov commented 5 years ago

( @jcouv If interop with existing deep learning libraries is a goal, probably conversion to/from negative indices would bubble up somewhere (in the client code?) anyway )

xCyborg commented 5 years ago

@xCyborg

This has already been explained. Indexes refer to the position between the elements and don't carry an implied directionality. So when you use an index as the end of a range it is exclusive of the next element after that index.

This behavior is consistent with other languages that support ranges and slicing with negative indexes:

>>> list = (0, 1, 2, 3, 4, 5, 6, 7, 8)
>>> list[-2]
7
>>> list[3:-2]
(3, 4, 5, 6)

No that you bring it up, I like [-2] better than [^2] But there is still an inconsistency, in normal indices the first element is [0] so it stands to reason that the last element should be [^0] and not [^1]

popcatalin81 commented 5 years ago

Yes, consistent or not with other languages, this is not consistent with itself (and neither are others)

[0] // First element, Offset 0 from start
[0..^0] // this works
[^0] // Should be last element, Offset 0 from end. This throws!!!!! <---- Language Weirdnes

Maybe would have helped to remove the weirdness if the indexer syntax was slightly different, something like:

[0..^0) 
[^0] // Now it would make sense for this to throw