Hacker News new | ask | show | jobs
by dbandstra 1999 days ago
I wouldn't call that careless. So far, proposals to extend the for-loop have not passed the cost/benefit test. I think supporting iterators would require something like magic-methods, which so far are not a thing in Zig (language syntax doesn't interact with userland code).

The status quo for iterators is this:

  var it = getSomeIterable();
  while (it.next()) |item| {
      ...
  }
IMO it's "good but not great" (not great because it adds a var and the variable pollutes the outer scope). But the alternative is a bit of a pandora's box.
2 comments

Honestly, I know it's minor, but when I tried Zig the fact I couldn't do the classic for loop to do a "for (int i = 0; i < n; ++i)" was quite annoying. It pollutes the scope, so you need to add scope braces every time, and I find it's much easier to forget the ++i at the end of the while loop.

I understand where it's coming from, but having something like "for (i32 i in [0,n[)" would be so much nicer in my opinion.

> and I find it's much easier to forget the ++i at the end of the while loop.

You can do:

    var i: usize = 0;
    while (i < 10) : (i += 1) {
        ...
    }
or

    var i: usize = 0;
    while (i < 10) {
        defer i += 1;
        ...
    }
although the two have slightly different semantics (the latter would increment even after a break; the former wouldn't).
Given how Zig does not allow variable shadowing (which is a choice which makes sense on its own) I find the scope leak to be very annoying in practice.
Neither of those fix the scope leak, tho.
This is true. There have been proposals to alter the `while` syntax to fix the scope leak, as well as proposals to extend `for` to support ranges, but none have been accepted (and they aren't very compelling in my opinion).

Honestly, I'm not sure why people think it is such a big deal. I've written a lot of zig, mostly gamedev stuff, and I rarely use that construction. I find that the vast majority of loops are over arrays or slices.

While I don't have much experience with Zig, I agree -- from my little bit of playing around, it also hasn't been a big deal.

(The biggest thing I've hit that I want is a way to return information with an error. If that were fixed, I think Zig's error handling would be perfect.)

Loris Cro did a great talk on error handling in Zig, which also deals with how to return information with an error if you need to, and why the common case optimizes for simpler error values: https://www.youtube.com/watch?v=TOIYyTacInM
To me the scope leak was annoying because how Zig does not allow shadowing, so I have to write:

  var it = a.getSomeIterable();
  while (it.next()) |item| {
      ...
  }

  var it2 = b.getSomeIterable();
  while (it2.next()) |item| {
      ...
  }
You can do:

  {
    var it = a.getSomeIterable();
    while (it.next()) |item| {
        ...
    }
  }
  {
    var it = b.getSomeIterable();
    while (it.next()) |item| {
        ...
    }
  }
Yeah, it's not that common in general, I agree. I think my impression was coloured by the fact I tried Zig to implement a binary protocol deserialiser where getting the size first then iterating happened a lot.
The pattern I use for that is:

  get size
  allocate slice
  for(slice) |*elem| elem.* = read()
Both forms feature "implicit flow of control", contrary to Zig's stated goals. In "while (i < 10) : (i += 1)", it's hard to understand what the heck "(i += 1)". The best concept one might get is that it's puposely made be different from C, just to be different and confuse people.

In second case, it's "defer", coming from Go, the language which chickened out to add normal exceptions, because they're "implicit transfer of control", and LOLishly added "defer", as if it's not such.

> Both forms feature "implicit flow of control", contrary to Zig's stated goals.

They absolutely don't. All control paths are explicitly represented by syntax (no different from the hidden goto in a while loop -- it's explicitly recognised by the while syntax); proof: you can draw all of them by just examining the syntax of the current subroutine, while knowing nothing about others. Exceptions, however, are implicit: any call, foo(), might or might not cause some control flow change in the client without there being any explicit acknowledgement of that by the client; you cannot draw all the flow paths just by examining the syntax of the current subroutine.

Sorry, but throwing an exception in a function call is equivalent to:

    res, exc = fun();
    if (exc) goto exception_handler;
That's underlying model of how exceptions behave, and how they're implemented "manually" in languages with no exception handling (C, Go). You absolutely can draw that by examining syntax of a subroutine, and it's no more implicit than "defer".
Right, both the C style and Zig's defer are explicit, as opposed to exceptions, which are implicit, only Zig's error handling is less error-prone (it forces you to handle errors) and makes the code more readable, IMO, than the C style.
> while (it.next()) |item| {

Talk about confusing, implicit syntax of Zig - there's no connection between "it.next()" and "|item|". It's implicit and you need to make it up. Compare that to other languages: "for item in it" or "while (item = it.next())".

It isn't confusing. The meaning of the syntax isn't obvious if you didn't bother to learn zig syntax, but it is entirely consistent with how zig does this sort of thing.

The for loop payload syntax, where you can also give a name to the counter, is the only weirdness with it.

I'm sorry, but I do programming languages for decades, and familiar with a bunch of them. There's absolutely nothing special about Zig, it's yet another wannabe proglingo. What sets it apart is strong desire "to replace C" while being apparently "as different as possible from it".

I understand such a strategy (among many possible), and that will be a fun strategy if it pans out. The current situation though is that people question: why that dude purposely make it harder than needed for everyone?

> There's absolutely nothing special about Zig

That's just not true. It is not only pretty exceptional in its general partial evaluation construct combined with compile-time introspection (although D and Nim have some similar features), it is the only language AFAIK where a single general partial evaluation construct is used in stead of type generics, value generics, typeclasses/concepts, constexprs, macros and conditional compilation.

Zig might not be your cup of tea, and that's fine, but it is without a doubt unique in its design and not only special, but pretty radical.

> Talk about confusing, implicit syntax of Zig

I don't think it's any more confusing than any other syntax (and less confusing than C's `for` syntax where the ubiquitous `;` is treated differently than anywhere else), even though it might not be familiar to you before learning it, but there is absolutely nothing implicit here. You might find `in` more appealing to you or more familiar than `|` but it's certainly not more explicit.

I feel like what you're complaining about is the equivelent of Calculon's critique of a screenplay: "no, I don't like the font". I am very much not a genius (check the user name) and it took me less than a minute to understand the payload operator (which IIRC was taken from Ruby or something).

Let's say `for (x in y) {}` replaced the payload operator. It would still exist in:

- if(some_error_union) |value| {} else |err| {}

- if(some_nullable) |value| {}

- while(some_nullable) |value| {}

- switch(some_tagged_union) { .some_tag => |value| {} }

And any that I might be forgetting. Is your suggestion that each of these should have separate syntax, thus complicating the language with one-off syntax?

> I feel like what you're complaining about is the equivelent of Calculon's critique of a screenplay: "no, I don't like the font".

Syntax is not irrelevant to the main use of a programming language the way fonts are to the use someone is probably concerned with if they ask an opinion of a screenplay, so, no, I don't see a similarity there.

> it took me less than a minute to understand the payload operator (which IIRC was taken from Ruby or something).

Loosely inspired by Ruby, perhaps, but definitely not taken from. Neither the positioning nor the semantics are the same.

> I feel like what you're complaining about is the equivelent of Calculon's critique of a screenplay: "no, I don't like the font"

I'm a human, and syntax is very important to me. 't broken read can syntax I. In that regard, if you purposely (or naively) misuse Comic Sans, then yes, "I don't like the font".

> (which IIRC was taken from Ruby or something).

Bingo. Ruby's not everyone's favorite language, so please be ready that people will send you rays of some hard love in that regard. Did you consider using C syntax for C-replacement-wannabe? (Rhetoric question.)

From what you're saying, I believe you would complain about a foreign language simply because it doesn't resemble your native language enough. Zig's syntax choices are not arbitrary and great effort is made to ensure their consistency. Your complaints about the syntax aren't that there is anything inconsistent or illogical about the syntax, just that it isn't the syntax you're used to.

That's a valid reason to not want to use the language, but I don't believe it is a valid basis for criticism and certainly doesn't excuse the hostility of your comments in this thread.