Why zip behaves differently in list comprehension?



Here is what I got:

julia> for i in zip(1:3, 2:5)
(1, 2)
(2, 3)
(3, 4)

julia> [i for i in zip(1:3, 2:5)]
ERROR: DimensionMismatch("dimensions must match")

Why zip works differently in list comprehension? Is this a feature or a bug?


I’d say this is a bug because

julia> length(zip(1:3, 2:5))

julia> eltype(zip(1:3, 2:5))

makes pretty clear what collect should do.



The problem is that zip(1:3, 1:4) declares to have a well defined shape but errors when one asks what it is:

julia> itr = zip(1:3, 1:4)
Base.Iterators.Zip2{UnitRange{Int64},UnitRange{Int64}}(1:3, 1:4)

julia> Base.IteratorSize(itr)

julia> size(itr)
ERROR: DimensionMismatch("dimensions must match")
 [1] promote_shape at ./indices.jl:87 [inlined]
 [2] size(::Base.Iterators.Zip2{UnitRange{Int64},UnitRange{Int64}}) at ./iterators.jl:290
 [3] top-level scope at none:0

julia> length(itr)

Not sure what’s a good fix: the one dimensional case is easy, but the multidimensional case not so much:

collect(zip(rand(2, 3), rand(2, 3)))

For things to be type stable it should return a matrix. However what shape should the matrix have is a bit unclear, so maybe this needs to error.


This could be utterly wrong because I didn’t analyze impact to other areas!!! (and it is just draft!)

julia> import Base.axes

julia> axes(z::Base.Iterators.Zip2) = min(axes(z.a), axes(z.b))
axes (generic function with 29 methods)

julia> [i for i in zip(1:3,2:5)]
3-element Array{Tuple{Int64,Int64},1}:
 (1, 2)
 (2, 3)
 (3, 4)

julia> collect(zip(rand(2, 3), rand(2, 3)))
2×3 Array{Tuple{Float64,Float64},2}:
 (0.67172, 0.578706)    (0.745477, 0.868811)   (0.759369, 0.390175)
 (0.786294, 0.0748731)  (0.0990557, 0.746172)  (0.160871, 0.711756)

I was inspired by:

length(z::Zip2) = _min_length(z.a, z.b, IteratorSize(z.a), IteratorSize(z.b))

which also means that draft above has to check (at least) infinite cases!