Unexpected (to me) Array behavior

It is a collection of elements. The confusion comes because you think Collections are indexed from zero: that is, that collection[0] is the first element. In Swift, this is not true. The valid range of indices for a collection runs from startIndex to endIndex.

In fact, the only Collection that is guaranteed to have 0-as-start 1-as-step is Array. Data does come close, but falls short as it is its own subsequence.

Edit:

Now that I checked the Array doc, it actually doesn't say anything about the nature of startIndex and its step function :face_with_monocle:.

1 Like

In my mind, that's a calamity. The idiom of getting to the first element of an array with arrayname[0] is a 35 year old habit.

Wait, what? All we are talking about here is arrays. The examples are array literals and simple declarations like [Int].

It becomes a 35-year-old implementation burden though, as all kinds of collection needs to figure out how to make it works with that paradigm.

ArraySlice :wink:.

And it works fine. The above code isn’t getting the first element of an Array, it’s getting the first element of an ArraySlice. Different thing. If you want an Array, you can wrap whatever you have in Array().

With that said, I should clarify that I totally understand where you’re coming from. Lots of users have a trajectory into Swift where they enter the language, pick up Array as the first collection they use, and instantly subscript it. This appears to work, so they are not forced to understand Swift’s Collection model.

In many ways, the real mistake was to make Array‘s Index Int. This was done because Swift had no notion of offset-based indexing (and indeed still does not, though it’s in progress: see Offset Indexing and Slicing). If Array’s Index were opaque, it would force users to confront their understanding of what the system actually does.

And the system is defined sensibly. Index is not intended to be a convenient type for the user, it’s intended to be a convenient type for the Collection. The goal of Collection.Index is, roughly, if you have one you should be able to subscript Collection with it and get the element in O(1). For Array, an Int is fine. But for other Collections we use other types. Dictionary uses an opaque type that ends up holding an offset into the Collection. NIO’s CircularBuffer does a similar thing. You can construct weird Collections over things like the getaddrinfo list by making the Index type a pointer.

This solution is very flexible, but without offset-based indexing it bumps into the confusion boundary for many users very quickly. The Swift community really ought to push offset-based indexing over the line to help avoid these problems.

If an ArraySlice is returned from that expression, that's a magic expression. It breaks the principle of least astonishment.

1 Like

Ehh, that'd be a hard call. Given that Swift subscript rarely returns the same type. Admittedly it can look deceiving coming from other languages, but that's actually the most consistent as part of the language.

1 Like

I'd agree with the idea that if there is some amazing feature you get from having an opaque index, great. But don't call it array, and not expect people to be pissed off if it doesn't look like every other array they've dealt with.

I mean, again, Array behaves the way you want it to. ArraySlice does not.

1 Like

I don't think it's too hard a call, I am pretty damned astonished.

Sure.

I understand that, my problem is that I never would have expected what looks like a basic syntax to return a new type, and especially not a new type with a strange behavior. I can't think of another language which does this, why would anyone expect it?

I don’t think the situation is quite as extreme as you are making out. Lots of basic syntax returns new types in all kinds of languages. So let’s restrict ourselves to slicing. Go and Rust both pervasively return new types on slicing. Python and most interpreted languages allow it too, though it is uncommon. Broadly speaking, languages that fall into the camp of both “strongly typed” and “not focused on immutability” tend to have this behaviour.

3 Likes

I'm pretty sure slicing syntax is a (relatively) recent invention. I don't think you can get anywhere near as concise with C, C# (Ok, maybe 8.0), or Ruby. Ones in my recent memory would be Go, Kotlin, and Python. Even Python refuses to call whatever they're slicing array.

Perl allows a programmer to define the starting index for all arrays. Pascal allows the programmer to define the valid range of indexes on a per-array basis (fixed size), and the range need not start with zero.

And those are arrays, not types that look like arrays.

1 Like

Perl has learned its lesson

As of Perl v5.30.0, or under "use v5.16", or "no feature
"array_base"", $[ no longer has any effect, and always contains
0. Assigning 0 to it is permitted, but any other value will
produce an error.

This is not the first, nor the last thread, let me link this here [Rant] Indexing into ArraySlice

1 Like

Before diving in. In there are

  • The offset camp that thinks every collection should index like array,
    • startIndex will just bloat the code base and easy to forget,
  • The index camp the think it’s better to truly reflect how collection works inside,
    • Offset-based would have hidden perf impact, and that you should actually think with index, not offset,
  • Someone propose offset design, and the discussion evolves into into discussion about that design.

Lemme see how right I am. This’s been rehashed enough that I’m pretty sure this is accurate. Oh, and the op is vehemently in the offset camp to boot

There is an obvious design deficiency in Swift that breaks people's expectations, and leads them to this forum in frustration.

There is a "camp" that learnt model for array and array slice from other languages. Swift on the first glance adheres to that model. And continues mimicking that model, while underlying concepts are different. Then Swift takes little to no effort to transition user from established model: the first wtf moment is a runtime error. And then user is left with no support until the next runtime error.

Terms of Service

Privacy Policy

Cookie Policy