New array declaration syntax

I agree that this is very clear. I think the main downside is that it’s a major departure from e.g. C and current Stan. All in all, I’d be happy with it.

Edit, to give some context for this design space:

Fundamentally “array” is like a function that takes a size N and a type T and returns a new type. That’s called array’s kind signature, analogous to a function’s type signature. We’re talking about syntax for type application, which is analogous to function application.

So in array[2] matrix[2,2] you can say that array[2] is a partially applied type which is then applied to matrix[2, 2], and the syntax for type application is juxtaposition with a space. That’s analogous to writing function application f(x) as f x (which some languages do).

One thing to consider is that using ’ ’ for type application doesn’t let you explicitly specify associativity or precedence, which we’ll run into if we add more parametric types in the future. But that’s a crossable bridge if we get to it.

4 Likes

I like this a lot. I don’t think it’s a big deal that it deviates from C (most of our users can’t program in C. For many of them Stan is the only explicitly typed language they use). It is a big change from Stan but I’m assuming this is a 3.0 change.

3 Likes

Would function signatures use the extra array keyword?
The ODE solver takes a function that looks like this:

real[] ode_system(real t,
                  real[] y,
                  real[] theta,
                  real[] x_r,
                  int[] x_i);

So that would change to

array[] real ode_system(real t,
                        array[] real y,
                        array[] real theta,
                        array[] real x_r,
                        array[] int x_i);

It’s quite verbose but I’m in favor. The change would eliminate the confusing notation where vector[3] is a vector but vector[] is an array.

real func(vector x, // a vector
          vector[] y // array of vectors, not a vector!
          ) {
  vector[3] z = y[1];
  ...
5 Likes

I am also in favor. While it is a bit verbose, its a lot less confusing and I would always take clarity over brevity if its one or the other. Much less support troubles also. I feel like a lot of user issues on the forums come down to indexing issues.

But yes, this is probably a Stan 3 change. Though we could keep the old syntax and this one, but that might be too much of a maintenance burden?

Just to make stuff a bit more complicated, I don’t really dislike the way it is now and I am not convinced any of the proposed solutions are clearly and obviously always better (I would say that probably some are slightly better most of the time). So the conservative in me would probably oppose all of the options.

Is there a more detailed description of the problem this change is trying to solve than “The current syntax for declaring an array of matrices is not ideal.” ? If the current syntax is confusing I would expect we have a ton of posts where users are confused about this - do we have those? What do they find confusing? Could we run the proposed options by some Stan novices to see what they think about it?

I don’t think this specific change is a big deal, I just feel like we are moving quite quickly on quite a lot of syntax changes and the process is primarily based on feelings and personal opinions of a relatively small group of people…

Well, there’s also “The problem is that the type is split into matrix[I,J] and [N].”

Did you look at the tuples proposal? That That proposes adding declarations like

(int, real, vector[4]) tup;
tup.1 = 2;
tup.2 = 20.34;
tup.3 = [1,2,3,5]';

What if you need to put an array in a tuple? Is it

(real[3], vector[1]) tup;
tup.1 = {1.1, 2.3, 4.0};
tup.2 = [1]';

and if that’s acceptable then can you also write

real[3] r;
r = {1.1, 2.3, 4.0};

So here we are.

Not a ton but we see them once in a while. Here’s the latest example, from a week ago.

I agree, this is a problem. The tuples and structs design doc even says it’s a hasty copy&paste, “no need to review yet” and yet people seem to think it’s all agreed on and close to being implemented?

3 Likes

Thanks, I missed the connection with tuples - that’s a very good reason. Thanks for linking to example of users being confused. Forgot to say that I am glad you are working on this. My previous comment reads harsh and I am sorry for that.

Since I have nothing better to do, I’ll also propose something - I like stuff being explicit, but the array keyword seems a bit annoying. Couldn’t we just support brackets in types? I.e.:

(matrix[2,3])[4] a; //same as matrix[2,3] a[4] in the current syntax
matrix[2,3,4] a2; //Error - not allowed.

//The following two are equivalent
(real[2])[3] r1;
real[3, 2] r1;

There is a slight conflict with the proposed tuple syntax - but maybe treating 1-tuple as the type itself is actually OK? (The tuple doc doesn’t seem to have opinion on 1-tuples).

Hope I am not missing something basic - I should know better than to get involved in language design (which I don’t really understand), but it is so much more fun than the stuff I’m supposed to be doing…

To be constructive: I don’t have any Stan novice handy, but I can show the proposals to a few programmers that’ve never seen Stan and see which seems the most intuitive to them, as an additional source of information.

2 Likes

You apologize a lot. I didn’t think it was harsh.

Good idea.

I think it’s more than just matrices. Declaring arrays of anything is confusing (example below).

Definitely more verbose but it’s not horribly verbose and it’s so crystal clear! I’m in favor too.

Yeah I’ve always found this confusing and hard to explain/justify to new users.

Yeah that’s a good idea!

Anecdotally, if I teach a Stan workshop and show the data block for a logistic or poisson regression:

data {
  int N;
  int y[N];
  vector[N] x;
}

then I will either have to take a substantial amount of time to justify why the [N] goes in different places or tell them to ignore it. Of course I could instead use

data {
  int N;
  int y[N];
  real x[N];
}

and then it seems more reasonable to them but then I’m back in the same spot the minute I need to use a vector for something later.

If instead the syntax were something like

data {
  int N;
  array[N] int y;
  vector[N] x;
}

then the only clarification I would need to give is that int is necessary because arrays could be real or int. Otherwise everything is pretty clear I think.

6 Likes

this would be great - difference between functions decl syntax and variable decl syntax is most annoying

7 Likes

My 2 cents as a fairly new stan user: I voted for the matrix[N; I, J] syntax because it preserves the index ordering (which I very often get confused) and doesn’t introduce any ambiguity between e.g. vector[N][I] and vector[I][N]. Because of this ambiguity and because it mimics C, I also find the existing syntax preferable to matrix[I, J][N], I’m surprised this got so many votes. However, I agree with the recent comments that explicit declaration of an array as array[N] matrix[I, J] might be the best, but matrix[N; I, J] is more concise.

4 Likes

So I got a few impressions from two non-Stan users and one random user of the forum. I messed up a bit and failed to have array[N] matrix[I,J] as a possibility. Generally, regardless of the syntax people expected the order the indices are written when declaring a variable to match the order when using the variable. Only explicit syntax prevented this sometimes. I think this supports the array[N] matrix[I,J] syntax, because (although I didn’t test it explicitly), it maintains the order of indices.

Methods & Results

This was the prompt I gave to people:

In the Stan programming language, you can define a matrix this way:

matrix[2, 3] m;

In this case

  • m[2, 3] is of type real
  • m[3, 2] is an “out of bounds” error and
  • m[2] is of type row_vector[3]

For each of the following hypothetical declarations of the variable x:

  1. matrix[4][2, 3] x;
  2. matrix[2, 3][4] x;
  3. matrix[4, 2, 3] x;
  4. matrix[2, 3, 4] x;
  5. matrix[4 ; 2, 3] x;
  6. matrix[2, 3; 4] x;
  7. (matrix[2, 3])[4] x;
  8. array<matrix[2, 3], 4> x;
  9. array<4, matrix[2, 3]> x;

What would you guess is the type of x[4]?

a) error - index out of bounds
b) matrix[2,3]
c) matrix[3,2]

Except 7,8,9 everybody guessed a) when 4 was first in declaration and b otherwise. For 7,8,9 each had two users guess b and one user (different) to guess a.

5 Likes

I like that it’s compact. The drawback is that vector[I; N] and matrix[I, J; M, N] aren’t transparent.

I think the survey is too leading using 4 for a declaration dimension and the index in x[4]. Cases 3 matrix[4, 2, 3] and matrix[2, 3, 4] are particularly confusing because it’s unclear to me if the intent was two different orders and the answer is supposed to be matrix[2, 3] in both cases. I think it’d be better to ask the type of x[j] for declaration matrix[A, B, C] x;.

It also doesn’t cover the ambiguous cases of vector[I][M] or matrix[I, J][M, N], which are the ones I’d worry people would confuse.

The answers are also too limiting in that you can triangulate.

How many people responded?

Given that my suggestion (7 without parens) is confusing people, how about

real x[...] ===> array[...] real x;
vector[N] x[...] ==> array[...] vector[N] x;

Even more natural in terms of language spec and implementation would be

array[I] of array[J] of matrix[M, N]

so we could think of

array[I1, ..., IK] foo[...];

as shorthand for

array[I1] array[I2] ... array[IK] foo[...]

I would be OK with the array[I, J] matrix[M, N] syntax, which would reduce what we now have as real x[N] to array[N] real x;, I do not like any of the other proposed

4 Likes

I’d like to get a consensus here so that I can move forward with the implementation. It sounds like array[N] matrix[I, J] has some fans, but it wasn’t in the original poll. Here’s a new poll with that one vs. the winner from the original poll:

How to declare an N-sized array of I × J matrices?

  • matrix[I, J][N]
  • array[N] matrix[I, J]

0 voters

I’m going to ping everyone who voted in the original poll because I’m a terrible person: @mitzimorris @syclik @Max_Mantei @spinkney @jtimonen @adlauretig @martinmodrak @fabio @rtrangucci @MauritsM @paul.buerkner @mbjoseph @srouchier @RJTK @Bob_Carpenter

6 Likes

Thanks for following up on this @rybern.

Good call. There’s too much traffic on the forums now to be sure people will see it, so ping away!

sounds good - I stand by my vote!

If we are going with array[N] matrix[I, J] then that’s what we should use for real and integer arrays, too. So that’d be

array[N] real x;

not

real[N] x;
1 Like

Agreed. I’m imagining that we’ll support both, maybe depreciating the old one. We’ll also need syntax for an unsized version, which makes sense to be array[] for one dimension and array[][][].. (some might argue array[,,..]) for more dimensions.

3 Likes

Good point.

We’re using real[ , , ] for a 3D array function argument now. If we allow array[I, J, K] then I feel pretty strongly the unsized version should be array[ , , ].

5 Likes

I voted, as an end user.

4 Likes