I think any loop which handles these different-type vectors in succession will be type-unstable. You can still speed it up by being more explicit, and IMO it’s much more readable to write for loops than this nested stack of iterators & broadcasting.
But if the loop is over exactly 8 things, you can also just write them all out. Again much more readable, hardly more code. This is type-stable and much quicker. (You could generate the same code via metaprogramming, e.g. with Base.Cartesian.@nexprs or some @unroll package, but for 8 things it’s unlikely your reader will thank you.)
function loop_param() # Not type-stable, but quicker and much more readable
asc = 4:7
desc = 4:-1:1
static = fill(4, 4)
# out = [Tuple{Int,Int}[] for _ in 1:8] # option 1
out = [[(0,0) for _ in 1:4] for _ in 1:8] # option 2
i = 1
for coll1 in (asc, desc, static), coll2 in (asc, desc, static)
# append!(out[i], zip(coll1, coll2)) # option 1
copyto!(out[i], zip(coll1, coll2)) # option 2
i += 1
i > length(out) && break
end
out
end
function noloop_param() # This is type-stable, and much quicker
asc = 4:7
desc = 4:-1:1
static = fill(4, 4)
[
collect(zip(asc, asc)),
collect(zip(asc, desc)),
collect(zip(asc, static)),
collect(zip(desc, asc)),
collect(zip(desc, desc)),
collect(zip(desc, static)),
collect(zip(static, asc)),
collect(zip(static, desc)), # only 8 things. I wasn't careful about the order
]
end