Why differentiate between single-element collections and their contents?

Not complaining, just curious (really!)

Given the strong influence Matlab has had on Julia’s development, I was surprised to find that 1., [1.] and (1.,) were treated as different objects. In my specific use case, this is frequently a minor inconvenience, leading to lots of |> first, ... and (x,). I also thought that Matlab’s approach was an elegant solution to some of the paradoxes in numerical linear algebra (see this talk).

This was, of course, a very deliberate choice, so can anyone tell what was the thought-process behind it? Could anyone give an example of this distinction being useful?

Firstly I’d like to note that the title is wrong, you’re discussing single element collections, while zero-dimensional arrays are a different thing.

You’re basically asking “why does Julia, unlike Matlab, have scalars, instead of just vectors”. I think there’s many potential answers. An obvious one is performance, don’t use an array if you just need a scalar. But that’s not really a satisfying answer.

I think it boils down to the fact that Julia is a real/serious programming language, with a rich type system, which provides the programmer with power and safety.

6 Likes

To complement @nsajko s answer, this is one way to construct the 0D-array version:

fill(1.)

which is distinct from 1., [1.], and (1.,).

EDIT: Ah you also linked it, didn’t see that :sweat_smile:

1 Like

To realize just how vestigial Matlab is, note that a Matlab array can’t hold other arrays as elements. Julia is not really doing anything special by not following Matlab, quite the opposite.

Here’s an anecdote that illustrates how Matlab’s habit of playing fast-and-loose with array dimensionality can cause extremely nasty bugs. Not the same as single-element collections but in the same spirit.

I recently ported a colleague’s power distribution model from Matlab to Julia, partly because I needed to understand it line-by-line anyway and partly because I suspected this could bring nice improvements in performance. The code had a few simple calculations like mean(max(A)), where A is an n x m matrix of line current in n line branches and m time periods. By default max(A) in Matlab takes the maximum over each column, so the expression was meant to calculate the largest current in each branch and then return the average of these currents over time. This was repeated for many grid cells with various branch configurations.

But here’s the problem: many grid cells had only a single branch, so max() was taken over a 1xm matrix. In these cases, Matlab “helpfully” takes the maximum over the next non-singleton dimension instead. So the code ended taking the maximum current over the year, and then calculating mean() over that scalar value. So the whole model failed silently in all single-branch grid cells (roughly 10-20% of all cells), and the original developer never picked up on it.

So in my opinion Julia’s relative strictness is one of its best features. I can only imagine how many man-millenia have been lost in bug-hunting nasty Matlab code like this, all for the sake of negligible gains of convenience.

Oh - and as a bonus the Julia version ran 50x faster.

10 Likes

I think the real issue that Matlab helped with, way back in 1984, is that it was no longer necessary to cobble your test code that used BLAS and LAPACK together in C or Fortran, you could test it out in a REPL. It is a different thing now, they want you to think that you can ship applications written in Matlab (shudder) - but back then, you would likely take your algorithm and write it in C or Fortran. It was a testbed, and for that, it was great.

Julia clearly learned from this, and it is much better.

3 Likes

The best thing about Matlab back in the day was that every variable was a double-precision two-dimensional optionally-complex array. MATrix LABoratory, as the name says. No type or shape ambiguities at all! Even strings consumed 8 bytes for each character. Simplicity itself. Who needs integers, anyway?

1 Like

Could you give an example?

Thanks everyone! I did not intend to imply that having such a feature would be a good idea, I just could not think of any cases where not having it would be preferrable (at least in the context of scientific computing).

Interestingly, this ancient thread mentions another very compelling reason: multiple dispatch. As the Matrix and Vector types do not contain any information on size, it’s unclear how one would make specially-sized matrices and vectors behave differently (so as to make a 1x1 matrix behave like a scalar).

EDIT: Some other interesting threads from when they were deciding this kind of stuff:
1: feature request: automatically convert 1x1 matrix to a scaler number in proper cases #4797
2: embed tensor-like objects as higher-dimensional objects with trailing singleton dimensions #3262