# Array comprehension is inconsistent, given the column-major property of julia

**URL:** <https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946>\
**Category:** Internals & Design\
**Tags:** question, array\
**Created:** [January 8, 2026, 4:29am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946 "2026-01-08T04:29:45Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [January 8, 2026, 4:29am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/1 "2026-01-08T04:29:45Z")

</div>

Related doc: [Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-column-major)

For a matrix

```julia-auto
julia> x = [1 2; 3 4]
2×2 Matrix{Int64}:
 1 2
 3 4

```

The correct order of element is

```julia-auto
julia> for e = x
           println(e)
       end
1
3
2
4

```

To exactly reproduce the above sequence in a double-index fashion, we have to write

```julia-auto
julia> for c=1:2, r=1:2
           println(x[r, c])
       end
1
3
2
4

```

Now I’m expecting that

```julia-auto
[x[r, c] for c=1:2, r=1:2] == x

```

returns `true`, which is deemed **consistent**. But the existing behavior is not.

(I found this yesterday when I was thinking about the JuMP container [Specifying objective coefficient when creating a JuMP variable](https://discourse.julialang.org/t/specifying-objective-coefficient-when-creating-a-jump-variable/134923))

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [January 8, 2026, 5:21am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/2 "2026-01-08T05:21:36Z")

</div>

In both cases the fastest changing iterator is closest to the loop body, the comprehension is just written with the body first which is why it has the opposite order of the for loop. I think this makes sense somewhat although I also have to think for a moment first sometimes to pick the right order for each.

---

<div class="post-metadata">

**Author:** ![draftman9](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/draftman9/32/38128_2.png) [@draftman9](https://discourse.julialang.org/u/draftman9)\
**Post date:** [January 8, 2026, 5:26am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/3 "2026-01-08T05:26:24Z")

</div>

`[x[r, c] for r=1:2, c=1:2]`: r is the rows, c is the columns. It will give a multi-dimension array, where the index does not rely on each other/another/any other when the array is generating. Totally it is not the syntactic sugar of for loop.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [January 8, 2026, 6:20am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/4 "2026-01-08T06:20:08Z")

</div>

@WalterMadelim the other thing to keep in mind is: the behaviour is what it is. We won’t be changing it because it would be breaking, both in JuMP and in Julia. Just learn to live with previous decisions 😄

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [January 8, 2026, 6:37am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/5 "2026-01-08T06:37:21Z")

</div>

You’re suggesting that

```julia-auto
[x[r, c] for r=1:2, c=1:2] == x

```

where `==` reads “recovers”. Okay I think this make sense at the beginning for everyone, as it is highly intuitive.

The main concern is that

- It doesn’t co-exist well with julia’s column-major Arrays, which I pointed out in my #1 post.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [January 8, 2026, 7:23am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/6 "2026-01-08T07:23:05Z")

</div>

I think the crux of this topic is that:

for a julia Matrix A\_{rc}, whose element is called by `A[r, c]`, the `c` index is considered at a higher rank.

In this expression `for c=1:2, r=1:2`, the `c` index is considered at a higher rank.

The word “higher” is also perceived as being “outer”, “independent”, e.g. the month index relative to the day index (31 days in the first month, 28 days in the second month).

Therefore I think **in the julia world** , letting

> [@WalterMadelim](#):
>
> `[x[r, c] for c=1:2, r=1:2] == x`

be `true` should make more sense.

> **Some more instances I come up with**
>
> ```julia-auto
> julia> Calendar2026 = [[1, 2, "...", 31], [1, 2, "...", 28]];
> 
> julia> for m = 1:2 # 🟦
> for d = Calendar2026[m]
> println("month $m, day $d")
> end
> end
> month 1, day 1
> month 1, day 2
> month 1, day ...
> month 1, day 31
> month 2, day 1
> month 2, day 2
> month 2, day ...
> month 2, day 28
> 
> julia> for m = 1:2, d = Calendar2026[m] # 🟧 readily derived from the above
> println("month $m, day $d")
> end
> month 1, day 1
> month 1, day 2
> month 1, day ...
> month 1, day 31
> month 2, day 1
> month 2, day 2
> month 2, day ...
> month 2, day 28
> 
> julia> @assert [[d for d = Calendar2026[m]] for m = 1:2] == Calendar2026 # corresponds to 🟦, this is okay
> 
> julia> Calendar2026Matrix = [[1, 2, "...", 31] [1, 2, "...", 28]] # if organized into Matrix, we should end up with this
> 4×2 Matrix{Any}:
> 1 1
> 2 2
> "..." "..."
> 31 28
> 
> julia> for m = 1:2, d_ind = 1:4
> println("month $m, day $(Calendar2026Matrix[d_ind, m])")
> end
> month 1, day 1
> month 1, day 2
> month 1, day ...
> month 1, day 31
> month 2, day 1
> month 2, day 2
> month 2, day ...
> month 2, day 28
> 
> julia> [Calendar2026Matrix[d_ind, m] for m = 1:2, d_ind = 1:4] # fails to recover the primal
> 2×4 Matrix{Any}:
> 1 2 "..." 31
> 1 2 "..." 28
> 
> ```

Opened an issue

> <https://github.com/JuliaLang/julia/issues/60595#issue-3791727224>
>
> Please see this post https://discourse.julialang.org/t/array-comprehension-is-in…consistent-given-the-column-major-property-of-julia/134946?u=waltermadelim, and the #6 post under the same topic therein.

---

<div class="post-metadata">

**Author:** ![zdenek\_hurak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zdenek_hurak/32/53118_2.png) [@zdenek\_hurak](https://discourse.julialang.org/u/zdenek_hurak)\
**Post date:** [January 8, 2026, 8:21am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/7 "2026-01-08T08:21:27Z")

</div>

Identical issue has been discussed a number of times here. In particular, I recommend [Why column major?](https://discourse.julialang.org/t/why-column-major/24374), in which you can find a few useful explanations. Perhaps my take [Why column major? - #36 by zdenek\_hurak](https://discourse.julialang.org/t/why-column-major/24374/36) could be one of them.

---

<div class="post-metadata">

**Author:** ![nalimilan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nalimilan/32/147_2.png) [@nalimilan](https://discourse.julialang.org/u/nalimilan)\
**Post date:** [January 8, 2026, 9:03am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/8 "2026-01-08T09:03:12Z")

</div>

Also relevant is this post. 😉

> [@PSA: Julia is not at that stage of development anymore](https://discourse.julialang.org/t/psa-julia-is-not-at-that-stage-of-development-anymore/44872):
>
> You heard about this brand new exciting programming language called Julia and have some suggestions for how it should work. Wonderful! We’re happy to have you here and help you learn the language and listen to your ideas! However, please be aware that “new” is a relative term. Yes, Julia is newer than, say, C, which was first created in 1972 and standardized in 1989. It is not, however, new in the sense that it is still being freely designed and changed in arbitrary ways. Julia development start…

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [January 8, 2026, 9:17am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/9 "2026-01-08T09:17:52Z")

</div>

I agree with @jules that this convention makes sense when you switch from prefix `for` to postfix `for`. For me it’s natural to consider

```julia-auto
[x[r, c] for r=1:2, c=1:2]

```

to be equivalent to something like

```julia-auto
[(x[r, c] for r=1:2) for c=1:2]

```

(though this is already valid syntax creating a `Vector` of generators). I.e. here `r` changes the most rapidly.

On the other hand

```julia-auto
for r=1:2, c=1:2
    something_with(x[r, c])
end

```

is the same as

```julia-auto
for r=1:2
    for c=1:2
        something_with(x[r, c])
    end
end

```

where then `c` changes the most rapidly.

* * *

You could also consider `[x[r, c] for r=1:2 for c=1:2]` (without the parentheses resulting in generators), which for some reason behaves differently

```julia-repl
julia> vec([x[r, c] for r=1:2, c=1:2]) # as you would expect if you agree with my reasoning above
4-element Vector{Int64}:
 1
 3
 2
 4

julia> vcat([[x[r, c] for r=1:2] for c=1:2]...) # again, expected
4-element Vector{Int64}:
 1
 3
 2
 4

julia> [x[r, c] for r=1:2 for c=1:2] # unexpected
4-element Vector{Int64}:
 1
 2
 3
 4

```

I’m not sure what’s going on here.

```julia-repl
julia> ex = :([x[r, c] for r=1:2 for c=1:2])
:([x[r, c] for r = 1:2 for c = 1:2])

julia> ex.args[1].args[1]
:(((x[r, c] for c = 1:2) for r = 1:2))

```

* * *

> [@zdenek\_hurak](#):
>
> Perhaps my take [Why column major? - #36 by zdenek\_hurak](https://discourse.julialang.org/t/why-column-major/24374/36) could be one of them.

I don’t immediately see the connection with the topic at hand, so you should probably expand on this a bit.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [January 8, 2026, 11:44am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/10 "2026-01-08T11:44:25Z")

</div>

> [@eldee](#):
>
> ```julia-auto
> julia> [x[r, c] for r=1:2 for c=1:2] # unexpected
> 4-element Vector{Int64}:
> 1
> 2
> 3
> 4
> 
> ```

This is exactly where the current comprehension design is not consistent.

To follow the current comprehension grammar, one needs to remember the “folklore” convention

> [@jules](#):
>
> the fastest changing iterator is closest to the loop body

, which is a bit indirect by itself. And it clashes somewhat with

```julia-auto
julia> [(i, j) for i=1:3 for j=1:i]
6-element Vector{Tuple{Int64, Int64}}:
 (1, 1)
 (2, 1)
 (2, 2)
 (3, 1)
 (3, 2)
 (3, 3)

```

* * *

It’s also inconsistent in JuMP when we use different type of containers:

```julia-auto
julia> import JuMP

julia> model = JuMP.Model();

julia> JuMP.@variable(model, x[m = 1:2, d = 1:2]) # month, day
2×2 Matrix{JuMP.VariableRef}:
 x[1,1] x[1,2]
 x[2,1] x[2,2]

julia> for e = x # day-major (i.e. slowest changing)
           println(e)
       end
x[1,1]
x[2,1]
x[1,2]
x[2,2]

julia> JuMP.@variable(model, y[m = 1:2, d = ifelse(m>1, (1, 28), (1, 31))])
JuMP.Containers.SparseAxisArray{JuMP.VariableRef, 2, Tuple{Int64, Int64}} with 4 entries:
  [1, 1] = y[1,1]
  [1, 31] = y[1,31]
  [2, 1] = y[2,1]
  [2, 28] = y[2,28]

julia> for e = y # month-major
           println(e)
       end
y[1,1]
y[1,31]
y[2,1]
y[2,28]

```

* * *

I’m not sure if there would be any inconsistency _if_ the (convention-free) idea in my #1 post _were_ adopted. I’m not sure if that would be a more coherent design. Admittedly, I’ll have to get used to the existing behavior at the current stage.

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [January 8, 2026, 1:02pm UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/11 "2026-01-08T13:02:53Z")

</div>

By the way, this topic is essentially a duplicate of

> [@\`for i = 1:M, j = 1:N\` discrepancy (loop vs comprehension/generator)](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659):
>
> I noticed the following about the for i = 1:M, j = 1:N syntax. When using this syntax as part of a normal for-loop, j iterates first: julia\> for i = 1:2, j = 1:3 println((i, j)) end (1, 1) (1, 2) (1, 3) (2, 1) (2, 2) (2, 3) But in a comprehension/generator i iterates first: julia\> [println((i, j)) for i = 1:2, j = 1:3] (1, 1) (2, 1) (1, 2) (2, 2) (1, 3) (2, 3) I get that for i = 1:M, j = 1:N ... end is shorthand for for i = 1:M for j = 1:N ... end end, but it’s a bit confusing …

In particular, check out `okatsn`’s summary at the end.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [January 8, 2026, 2:20pm UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/12 "2026-01-08T14:20:55Z")

</div>

> [@eldee](#):
>
> is essentially a duplicate of

The discussion there is of high quality. But I think my depiction of this issue in my #1 post here conveys a sharper contradiction.

> [@eldee](#):
>
> `okatsn`’s summary at the end.

First of all, that post is quite lengthy and make use of `if-else` cases analysis. So the current design is intrinsically involved.

I think it doesn’t make sense to explain or understand it. What we can do in practice is just to _use them warily_—just like from

```julia-auto
x, y = a, b
...

```

to

```julia-auto
let x, y = a, b
    ...
end

```

, where the `let` stealthily modifies the coder’s intention, which is unfortunate.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 8, 2026, 7:15pm UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/13 "2026-01-08T19:15:34Z")

</div>

Simpler example that intends 1-based indices of 2 rows and 3 columns. These are “consistent”:

```julia-auto
julia> for c in 1:3
         for r in 1:2 # iterate rows directly in column-major
           println((r, c))
         end
       end
(1, 1)
(2, 1)
(1, 2)
(2, 2)
(1, 3)
(2, 3)

julia> for c in 1:3, r in 1:2
           println((r, c))
       end
(1, 1)
(2, 1)
(1, 2)
(2, 2)
(1, 3)
(2, 3)

julia> [(r, c) for c in 1:3 for r in 1:2]
6-element Vector{Tuple{Int64, Int64}}:
 (1, 1)
 (2, 1)
 (1, 2)
 (2, 2)
 (1, 3)
 (2, 3)

```

but this is not. It looks row-major after the last 3 examples, but we’re actually iterating `c` across rows directly instead:

```julia-auto
julia> [(r, c) for c in 1:3, r in 1:2]
3×2 Matrix{Tuple{Int64, Int64}}:
 (1, 1) (2, 1)
 (1, 2) (2, 2)
 (1, 3) (2, 3)

```

The reason is actually pretty intuitive: the array’s dimensions 3x2 match the order of the indices `(1:3, 1:2)`. If we want to emulate the first 3 examples where `r` represented rows, we have to reverse the order so the implied dimensions 2x3 also match:

```julia-auto
julia> [(r, c) for r in 1:2, c in 1:3]
2×3 Matrix{Tuple{Int64, Int64}}:
 (1, 1) (1, 2) (1, 3)
 (2, 1) (2, 2) (2, 3)

```

Let’s adhere to conventional matrix dimensions where rows are to the left of columns. Column-major array dimensions thus expand from left to right (rows → columns → beyond…) in the dimensions’ order, and the most efficient for-loop iterates indices from right to left (innermost loop over rows) in the reverse order. That’s the root cause of the “inconsistency” between for-loops and comprehensions. Breaking changes would just move that “inconsistency” somewhere else; I’d be fine with reversing the order of the comma-delimited for-loop and maybe the comma-less comprehension to innermost-first, but someone else would probably consider that inconsistent with the irreversible nested for-loop.

On the other hand, row-major array dimensions expand from right to left (beyond… ← rows ← columns) in the reverse order, so the for-loop would iterate indices from left to right (innermost loop over columns) in the dimensions’ order. The reverse order isn’t written as often e.g. aligning dimensions for broadcasting, so this is one of the aesthetic arguments in favor of row-major. I doubt even Julia v2 would make this big a change to `Array`; there’s just too much tradition in column-major matrices, even preceding Julia e.g. BLAS buffers, though it’s worth mentioning that transpose flags handle row-major matrices there (I believe at zero cost but I’m not sure).

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [January 10, 2026, 1:29am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/14 "2026-01-10T01:29:21Z")

</div>

I like Benny’s recap, which is quite clear.

* * *

I want to emphasize one thing:

- This topic is **not** a duplicate of the past discussions.

My idea is much simpler, which cares only the **front-end** behavior. To understand my claim, you don’t need any knowledge about “column-major” or the efficiency of the backend implementation.

My claim in the #1 post was _only_ suggesting that the final outcome should obey

 ![image](https://global.discourse-cdn.com/julialang/original/3X/8/7/87ad8fc991622f76706542db8e93cc7c3cf899cd.png)

, as a user I don’t need to look into how julia actually process that comprehension under the hood.

* * *

The pain with the current design is essentially caused by this redundant condition.

> [@Benny](#):
>
> The reason is actually pretty intuitive: the array’s dimensions 3x2 match the order of the indices `(1:3, 1:2)`

This mindset was essentially needless, and is incompatible with Julia’s choice on Array mechanism.

* * *

What I’m thinking currently:

Some constructs in julia are just not that perfect and 100% consistent. What practitioners can do, from the safest point of view, is just to bypass them. e.g. if we think carefully, it appears that not all of them are used frequently, e.g. `let`, `local` etc.

I think the basic `for` loop (not those in comprehensions) works good. And we have `function`s, that’s basically enough. Just like to communicate thoughts I only need a vocabulary about 3000.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 10, 2026, 2:51am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/15 "2026-01-10T02:51:56Z")

</div>

> [@WalterMadelim](#):
>
> > [@Benny](#):
> >
> > The reason is actually pretty intuitive: the array’s dimensions 3x2 match the order of the indices `(1:3, 1:2)`
> 
> This mindset was essentially needless, and is incompatible with Julia’s choice on Array mechanism.

You’re making a bad assumption here that the comprehension syntax is only about column-major `Array`s. We can easily replace the brackets `[]` with `()` and make a generator expression, which is not even an `AbstractArray`. There are no eager or lazy elements to index, but it _must_ be materialized into the same abstract array regardless of how the concrete array is implemented. Matching the comprehension indices to that abstract array’s indices (`axes` and `size` also work on generators) is the only sensible neutral option. It’d be difficult to convince people to reverse the comprehension indices just because it matches the innermost-row-iterating for-loops over a specific `Array` type; it wouldn’t match the innermost-column-iterating for-loops over row-major array types or for-loops over more exotic types, so why bother reversing it from its only meaningful property if it’d be generally “inconsistent” anyway?

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [January 10, 2026, 4:09am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/16 "2026-01-10T04:09:29Z")

</div>

> [@Benny](#):
>
> There are no eager or lazy elements to index, but it _must_ be materialized into the same abstract array

I didn’t get your idea.

In this topic I proposed only one concept: _self-consistency_.  
I didn’t find where generators can violate this self-consistency, since generators _cannot_ be indexed.

Maybe you can come up with an instance to illustrate?

* * *

I think the current Array design—the elements in the _first_ axis are organized contiguously in memory—is _reasonable_. The unfortunate point is that this style stands at the opposite of how we instinctively write normal loops

```julia-auto
for i = 1:I, j = 1:J, k = 1:K
    A[i, j, k]
end

```

Although this is unfortunate, I think it is reasonable, not “inconsistent”.

The real place where I find inconsistent is that Julia already goes with the “unfortunate” choice, **yet still** resort to the “instinctive” style of Array comprehension syntax

```julia-auto
[(i, j, k) for i = 1:I, j = 1:J, k = 1:K]

```

, which is perceived as an object that belongs to, e.g. Python (numpy).

* * *

Now my point is

- Can we make our syntax consistent, despite it being “unfortunate” already?

I think it is possible.

> [@Benny](#):
>
> if it’d be generally “inconsistent” anyway?

If you disagree, I’d like to see a code example.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 10, 2026, 11:05pm UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/17 "2026-01-10T23:05:05Z")

</div>

> [@WalterMadelim](#):
>
> My idea is much simpler, which cares only the **front-end** behavior. To understand my claim, you don’t need any knowledge about “column-major” or the efficiency of the backend implementation.

> [@WalterMadelim](#):
>
> The real place where I find inconsistent is that Julia already goes with the “unfortunate” choice, **yet still** resort to the “instinctive” style of Array comprehension syntax
> 
> ```julia-auto
> [(i, j, k) for i = 1:I, j = 1:J, k = 1:K]
> 
> ```
> 
> , which is perceived as an object that belongs to, e.g. Python (numpy).

You’re contradicting yourself here. The “front-end behavior” of multidimensional comprehension is _already_ independent of the “backend implementation” because its `for` syntax is based on the order of the dimensions/axes, but you’re insisting on a consistency with for-loops that _do_ depend on `Array`’s layout. A nested for-loop for a column-major array is not written the same way as a for-loop for a row-major array, and a for-loop for an array with contiguous elements along a middle axis won’t be written like either of those loops. It’s impossible to make comprehension’s `for` syntax consistent with efficient nested for-loops over arrays _in general_ (`eachindex` is a simple sequence that is not always as efficient as possible for a given concrete type), you can only be consistent with 1 particular layout’s. My earlier point was that the loops over row-major arrays just happen to align with the dimensions’ order, not that the loops are a better basis.

You mentioned NumPy, so I’ll note something funny here. 1-dimensional list comprehension is the only idiomatic option, so multidimensional NumPy arrays are converted from nested lists, the innermost list being a row. As a consequence of comprehension syntax being led by the element expression, the iterations are written in the reverse order of efficient for-loops over row-major arrays:

```py
>>> for r in range(1, 3): 
... for c in range(1, 4): # iterate columns directly in row-major
... print(r, c)
... 
1 1
1 2
1 3
2 1
2 2
2 3

>>> np.array([[10*r+c for c in range(1,4)] for r in range(1,3)])
array([[11, 12, 13],
       [21, 22, 23]])

```

So even a language whose comprehension’s `for` syntax _is_ based directly on nested for-loops doesn’t have your idea of “consistency”. Earlier when I said I could accept reversing orders in nested for-loops, I was following this lead.

> [@WalterMadelim](#):
>
> I didn’t find where generators can violate this self-consistency, since generators _cannot_ be indexed.

The absence of indexing is irrelevant. A generator represents a lazily iterable version of a comprehension by definition, they share the same syntax, and comprehensions lower to `collect`ing a corresponding generator. Since a generator can be used to fill an array of any layout, the syntax has a fundamental reason not to be based on any particular array layout.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [January 11, 2026, 2:15am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/18 "2026-01-11T02:15:40Z")

</div>

Considerations are already abundant.

To me, the simplest way out is to write customized code using the ordinary for loop instead.

```julia-auto
julia> x = [1 2; 3 4]
2×2 Matrix{Int64}:
 1 2
 3 4

julia> for c=1:2, r=1:2 if x[r, c] > 0
           println(x[r, c])
       end end
1
3
2
4

julia> x_new_1 = [x[r, c] for c=1:2, r=1:2 if x[r, c] > 0]
4-element Vector{Int64}:
 1
 2
 3
 4

julia> function my_procedure(x)
           x_new_2 = Matrix{Int}(undef,2,2)
           for c=1:2, r=1:2 if x[r, c] > 0
               x_new_2[r, c] = x[r, c]
           end end
           x_new_2
       end;

julia> x_new_2 = my_procedure(x)
2×2 Matrix{Int64}:
 1 2
 3 4

```

I have no additional comments. Different new views from others may still be appended subsequently…

---

<div class="post-metadata">

**Author:** ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)\
**Post date:** [January 11, 2026, 11:45am UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/19 "2026-01-11T11:45:03Z")

</div>

FWIW, I think the relation to how some array implementation stores its data in memory is not so relevant for the discussion about the syntax.

To me the surprise here is, that the following things are different:

```julia-auto
julia> [println((i,j)) for i in 1:2 for j in 3:4]
(1, 3)
(1, 4)
(2, 3)
(2, 4)

julia> [println((i,j)) for i in 1:2, j in 3:4]
(1, 3)
(2, 3)
(1, 4)
(2, 4)

```

whereas the same things written as for-loops are the same:

```julia-auto
julia> for i in 1:2
           for j in 3:4
               println((i,j))
           end
       end
(1, 3)
(1, 4)
(2, 3)
(2, 4)

julia> for i in 1:2, j in 3:4
           println((i,j))
       end
(1, 3)
(1, 4)
(2, 3)
(2, 4)

```

I agree that this is a footgun. However, I also think that each of the constructs by itself makes sense:

1. Nested for-loops: trivially correct
2. For loop with comma: short-hand for the nested for-loop
3. comprehension with 2 `for`: simple rewrite of the nested for-loops
4. comprehension with comma: constructs an array/iterator with dimensions matching the iterators (oops different order than the other 3)

For me the fundamental reason was not stated clearly so far: The comma in the comprehension means something fundamentally different, then the comma in the for loops! The comprehension with comma is a completely different iteration construct conceptually. Using a comma for it is an abuse of notation, if you wish. To make this more explicit consider dependent loop bounds:

```julia-auto
julia> for i in 1:2, j in i:3
           println((i,j))
       end
(1, 1)
(1, 2)
(1, 3)
(2, 2)
(2, 3)

julia> [println((i,j)) for i in 1:2, j in i:3]
ERROR: UndefVarError: `i` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
 [1] top-level scope
   @ REPL[13]:1

julia> [println((i,j)) for j in i:3, i in 1:2]
ERROR: UndefVarError: `i` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
 [1] top-level scope
   @ REPL[14]:1

```

This shows directly that the comprehension with `,` is a fundamentally different construct. So much so, that I would have liked a different syntax for it just to avoid exactly the confusion that we discussed here in this thread. To emphasize again: The true origin of the confusion is not there is an inconsistency in Julia’s iteration/comprehension construct but rather that there are actually 2 conceptually different constructs that use very similar notation such that they can be confused for another.

Personally, I think it would have been a better choice to use `;` instead `,` in the comprehension because `;` is also used to separate dimension in array literals. This would resolve the issue discussed here because it much easier to accept that

```julia-auto
for i in 1:2, j in 3:4
    #...
end

```

and

```julia-auto
[#= =# for i in 1:2; j in 3:4]

```

are different constructs following different rules.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [January 11, 2026, 2:30pm UTC](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946/20 "2026-01-11T14:30:02Z")

</div>

Thanks. Your account is very well organized, I’m fully convinced.

The newly suggested `[#= =# for i=1:2; j=3:4]` reads much clearer so I can perceive that `i=1:2` is the inner layer and `i` alters more frequent than `j` does (and this corresponds to julia’s Array memory layout, which is the correct thing).

One more concern: how will the `if cond(i, j)` be inserted so that I can still perceive the hierarchy correctly from the syntax?

e.g. how to explain

```julia-auto
sum(x[i, j] if i>j for i=1:3; j=2:4)

```

or

```julia-auto
sum(x[i, j] for i=1:3; j=2:4 if i>j)

```

? (I think the latter style reads awkward—my eye needs to jump left first and then jump right)

Or, say, the front-end syntax should let people know its direct programming counterpart: is it true that the counterpart of the former `sum(...)` style corresponds to

```julia-auto
for j=2:4, i=1:3
    if i>j
       ....

```

? (as I currently understand).

Moreover, can we write

```julia-auto
sum(x[i, j] for i=1:3 if i>2 for j=2:4)

```

, which corresponds to

```julia-auto
for i=1:3
    if i> 2
        for j=2:4
            ...

```

?

* * *

Even going further to write something more crazy like

```julia-auto
maximum(x[i, j, k] for i=1:4; j=1:3 if i>j for k=5:6)

```

We need to stipulate the rule carefully.

[Next page](https://discourse.julialang.org/t/array-comprehension-is-inconsistent-given-the-column-major-property-of-julia/134946.md?page=2)
