Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> It's zip with an arbitrary amount of array parameters.

So… zip?

Python’s does that, and for most other langages you could use overloading, basic macros, or traits trickery to get there if you really wanted to support unreasonable widths (IME you almost never need more than 3, and combining two zip/2 works fine then).



There is no single "zip". Java's .zip() will work on two sources, as will C#'s Zip(). Haskell's zip is no different, only accepting two parameters. I don't know any language other than Python that shares Python's iterator zip() implementation.

In implementation, Python's zip will return a generator that is iterated over using the iterator functionality, while Zig's .zip is compiled as a loop. Python's iteration may be turned into a loop, it may be interpreted, or it may be turned into some other kind of bytecode, who knows. The standard cpython implementation is much more complex, though: https://github.com/python/cpython/blob/main/Python/bltinmodu...

Concatenating zip()s is an unnecessarily complex solution, both in terms of syntax and in code generated. In Python this may not matter because it's a relatively slow programming language in general (the language often being "glue between fast C methods"), but in Zig this can easily become untennable.

I also disagree that you don't need more than 3. As the article states, if you leverage array-of-structs rather than struct-of-arrays you can use this to "deconstruct" objects without paying the memory usage penalty of struct padding. The 15% wasted RAM in this example is relatively small compared to some real use scenarios; something as common as a 3D vector will often have a whopping 25% space waste.

Other languages allow this as well (and often using such iterations are much faster than zip()ing lists together) but the lack of guarantees and repetitive syntax becomes a pain.


> There is no single "zip".

Which means you can implement yours to fit your needs.

> I don't know any language other than Python that shares Python's iterator zip() implementation.

https://docs.rs/itertools/latest/itertools/macro.izip.html

> In implementation

Which is hardly relevant. Python's entire implementation has aims, means, and purpose with no relation to Zig's.

> I also disagree that you don't need more than 3.

Which is not what I wrote.

> As the article states, if you leverage array-of-structs rather than struct-of-arrays you can use this to "deconstruct" objects without paying the memory usage penalty of struct padding.

Sure? And the article uses an example with 3 values.

> The 15% wasted RAM in this example is relatively small compared to some real use scenarios; something as common as a 3D vector will often have a whopping 25% space waste.

It also could hardly be less relevant: it's an issue in an AoS structure because all your objects have that overhead, therefore that's your total overhead.

Here it's 15 or 25% padding in a single value within a stackframe. You're probably wasting more stackframe space due to the compiler not bothering reusing temporally dead locations.

And that's if the compiler reifies the tuple instead of eliding the entire thing.

> Other languages allow this as well

OK?

> (and often using such iterations are much faster than zip()ing lists together)

Until they are not.


> Which means you can implement yours to fit your needs.

Which this doesn't, as zip is an expression and multi-sequence loops aren't.

> https://docs.rs/itertools/latest/itertools/macro.izip.html

External libraries aren't part of a language.

> Which is not what I wrote.

I admit, I read over the "almost" in "you almost never need more than 3".

> It also could hardly be less relevant: it's an issue in an AoS structure because all your objects have that overhead, therefore that's your total overhead.

> Here it's 15 or 25% padding in a single value within a stackframe. You're probably wasting more stackframe space due to the compiler not bothering reusing temporally dead locations.

That's not true: arrays are byte-addressable so inside an array the alignment can be shorter. An array of 121 33-byte values is 3993 bytes in size, an array of 121 usizes is 968 bytes in size, and assuming enums resolve to 32-bit values an array of 121 enums is also 484 bytes in size. There is no overhead here.

This has advantages and disadvantages. Unaligned access is slower in general but in many cases and unaligned array can be faster because of how many of its entries can be loaded into the CPU cache. There's no definite advantage here in terms of CPU performance, but in terms of RAM usage there is.

> Until they are not.

When does a for loop ever become faster than a generator? The values being mapped over are already evaluated, there is no lazy loading+early stopping to take advantage of the generator.


Zig's "zip" is purely syntactical, where Python's zip is a generator. This is significant both in terms of performance (Zig wins) and flexibility (Python wins).

Unlike Python, you can't pass a zip generator around. It's just a for-loop. While zig loops are expressions, they only return a single value.


> This is significant both in terms of performance (Zig wins)

Does it, actually? Does Zig's built-in pseudo-zip outperform Rust's? Or C++23's?

> It's just a for-loop.

Except it's not "just" a for loop, it's a weird special case for a for loop. And one which is actively dangerous too.


> Except it's not "just" a for loop, it's a weird special case for a for loop.

You could also argue that a for loop which can only iterate over a _single_ sequence is a special case of a multi-sequence for loop.

> And one which is actively dangerous too.

In safe builds (ReleaseSafe, Debug) it will cause a controlled panic if the sequences are not of the same size. Most likely it's a logical bug if you iterate over two sequences of different sizes. In ReleaseFast the compiler will make assumptions to improve performance. If it's very important for your code you can force a certain code block to always have runtime safety. Yes, there are trade-offs, but I don't feel it's _unreasonable_.


> You could also argue that a for loop which can only iterate over a _single_ sequence is a special case of a multi-sequence for loop.

When a "for loop" in virtually every language is not multi-sequence, not really. People expect a "for loop" to be a certain kind of thing.


Please check the context, I was comparing to Python. And please chill out. It's only "dangerous" when you decide to run it that way.


[flagged]


Hi, no need to be so abrasive about it.

> Please respond to the strongest plausible interpretation of what someone says, not a weaker one that's easier to criticize. Assume good faith.

Python's zip function returns a generator. A generator in zig would look like a function pointer with a closure. If zig were to implement the Python-style "zip" function, constructing the closure, and iterating over the generator, would be significantly slower than the naked "just a for loop" that we see in TFA. And that's not even considering the tuple construction (oops, now we need an allocator) & unpacking.

Ergo, the zig-style "syntactical zip" is higher performance than the Python-style "functional zip". Even when you cut through the baseline performance differences between the languages.


> Hi, no need to be so abrasive about it.

That's just projection.

> Please respond to the strongest plausible interpretation of what someone says, not a weaker one that's easier to criticize. Assume good faith.

I'll get right on that as soon as you extend the same courtesy, which you have refused to do at every opportunity.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: