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> [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> 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:
- Nested for-loops: trivially correct
- For loop with comma: short-hand for the nested for-loop
- comprehension with 2
for: simple rewrite of the nested for-loops
- 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> 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
for i in 1:2, j in 3:4
#...
end
and
[#= =# for i in 1:2; j in 3:4]
are different constructs following different rules.