Why does `[42][]` work?

I’ve seen something like this in the JuliaAcademy Flux course:
[42][]

It returns 42 but it doesn’t seem to work with any other type of array, ie [42, 43][], nor [42 43][], nor [42; 43][].

What does this indexing do, more exactly? Thanks

2 Likes

When doing multidimensional indexing, you’re allowed to omit the index into “trailing singleton dimensions” i.e. dimensions which are at the end of an array’s dimensions and have size 1. In this case, [42] is a vector with one dimension of size 1. Therefore you’re allowed to not supply that index and treat it as if it were a scalar or zero-dimensional array for some purposes. This doesn’t work for [42, 43] because the size of the dimension is 2 rather than 1. It similarly doesn’t work for [42 43] which as a 1×2 matrix. The relationship is also symmetrical: you can also supply more indices than an array has dimensions—as long as the index value you supply is 1. So you can do [42][1,1,1,1]. The motivation for this behavior is that it is very often convenient to be able to treat an m×1 matrix as if it were a vector or vice versa.

12 Likes

Good question. It is performing indexing with no indices. This is the canonical way of accessing the only element in a 0-dimensional array or containers with exactly one element (like Refs).

But for indexing into arrays, we also support indexing with more or less indices than the dimensionality of the array if the indices are ones or if the dimensions over which they were omitted were “singleton.” For example, you can index into a three-dimensional array with only two indices if the last dimension is of length one:

julia> rand(2, 3, 1)[2, 3]
0.5122251693833899

julia> rand(2, 3, 2)[2, 3]
ERROR: BoundsError: attempt to access 2×3×2 Array{Float64,3} at index [2, 3]

Or you could index it with no indices if all dimensions are of length one:

julia> rand(1, 1, 1)[]
0.24567576998890917

(Of course, we also support linear indexing which is a special behavior when exactly one index is given.)

I had thought we had this documented in the indexing section, but it looks like it’s missing. We also need linear indexing there, too!

5 Likes

I imagine this made sense before we had very clever, generalized, abstract indexing constructs, but now I don’t think this is necessary. I think of it as a confusing vestigial quirk.

Eh, the confusing vestigial quirk is linear indexing. This is peanuts compared to that. For what it’s worth I tried to completely restrict indexing to either 1 or N indices not too long ago and it surprised me how much we leaned upon this (PR #20040).

Ignoring linear indexing for a moment, we essentially have two choices. We can either demand that there be exactly N indices to index into an N-dimensional array and error otherwise, or we can define some way to meaningfully “fill in” or “remove” the omitted or extra indices as appropriate. In both cases, we’ve taken the very strict approach that you can only do so when it’s completely unambiguous: there can only be one such value that could possibly be added or removed.

I’ve submitted a documentation PR that talks about index numbers a bit more — see if that would help assuage some of the confusion here.

https://github.com/JuliaLang/julia/pull/30736

3 Likes

Thanks for this writeup.

I understand it is unambiguous, but am still under the impression that it requires a runtime check (unless the array size is known to the compiler).

But my main problem is that it is easy to get buggy code that occasionally runs when missing or accidentally adding an index. I don’t think that trying to do something anyway when faced with the wrong number of arguments is good design for an API. I hope that eventually #20040 will resurface, for 2.0.

Yes, having the same semantics for linear indexing is also a wart, but IMO less so because of #20079. The only case when they coincide is for vectors, and then they coincide anyway :wink:

The array size check is implemented as a bounds check, so it can be elided — just like all bounds checking — with @inbounds.

Given that the resolution to #4774 was to formally allow vectors to participate in the linear algebra of matrices, and given that linear indexing allows 1-column matrices to be indexed like vectors, this additional rule adds the symmetry for vectors to be indexed like 1-column matrices. I don’t foresee it changing.

1 Like