4 ms·
And why change something like that? The world would be a better place, IMHO, if there were less different ways to write something from language to language. So
by spacedcowboy 2y ago
And why change something like that? The world would be a better place, IMHO, if there were less different ways to write something from language to language.
Sometimes it seems like the change is just to make it different, not better.
- IshKebab 2y agoSame reason Rust uses foo.await instead of await foo. It's clearly superior syntax. The whole point of Zig is to fix C's mistakes. I don't know why they'd repeat this one.
- moomin 2y agoI’ve gotta say, when I first learned Rust had gone its own way on await, I was heavily sceptical. But seeing the actual examples was pretty compelling.
- IshKebab 2y agoYeah I was skeptical too but it makes so much more sense. Now I'm constantly thinking "this is dumb" when using other languages with the "normal" syntax. I wish they'd got it right for (de)referencing too though.
- dcsommer 2y agoI think Rust could use some syntax improvements for pointer manipulation. It has suffix .await, so maybe the community is ready for suffix .* for dereference now too. Visually scanning left and right, along with the extra parentheses, makes pointer-based code in Rust worse than C++ almost. A fully left-to-right syntax would be amazing. EDIT: found https://github.com/rust-lang/rust/issues/10011 https://github.com/rust-lang/rust/issues/10011 and https://github.com/rust-lang/rfcs/pull/3577 https://github.com/rust-lang/rfcs/pull/3577
- throwawaymaths 2y agowhy is foo.await better? is it a method call? is it a property/field value? how am i to be indicated that there is an implicit suspend point when you .await?
- dcsommer 2y agoIt preserves a left to right reading of the code. You don't have to jump visually elsewhere.
- vvillena 2y agoYou will know because any text editor or IDE worth the name will light up that ".await" in such a way you will immediately know it's not a method call or a struct field. The entire construct, including the dot, is a postfix keyword.
- rezonant 2y agoIt's the difference between (await (await foo).bar).baz and foo.await.bar.await.baz -- which do you want to write?
- int_19h 2y agoIn general, the rule of thumb is that prefix and postfix operators don't mix well in a single expression - order of operations is confusing, and reading the code requires going back and forth to follow it. In the ideal world, we wouldn't have unary prefix operators at all, but unfortunately unary +, -, and NOT are prefix mostly because they were that in math notation and got grandfathered in (bonus points to Smalltalk here for going with consistency here - "not" and "negated" are regular nullary methods there so it's postfix throughout!). However, these are rarely themselves an operand of another unary operator, so you can mostly deal with this by giving postfix higher precedence than prefix in all cases, so at least it's a simple rule. But then for pointer dereference, it is in fact common to have the result of a dereference itself be an operand in the middle of another expression. So now you have some choices to make. If you make the pointer dereference prefix, then you don't need extra parentheses when applying other prefix operators to it, e.g. -*p or !*p. If you make it postfix, then you don't need extra parentheses when applying other postfix operators to it, e.g. (using Pascal-style ^ for dereferencing) p^[0] or p^.x. Alternatively, you could add special postfix operators that desugar into the combination but avoid those extra parens, which is what C did with -> for field access. (Technically, you could also make everything prefix instead, e.g. field access ALGOL 68 style: `month OF birthDate OF person`. But this is very counter-intuitive with indexing, and also makes code completion unusable, so it's not a serious option.)
- FlyingSnake 2y agoAt this point &var and *var are pretty much an established way across languages, like if-else. I don’t see any benefit of reinventing this paradigm.
- epolanski 2y agoThe fact that the author of Zig who's extremely well versed in C and C++ went this way should make you at least try to think why he went that way before dismissing it.
- FlyingSnake 2y agoSounds like Appeal to Authority fallacy.
- xigoi 2y ago> Copying bad design is not good design. —Andreas Rumpf
- nineteen96 2y agoAgreed. And p.* is remarkably unintuitive and nonsensical compared to *p.
- int_19h 2y agoIt's remarkably intuitive and sensible if you remember that . in Zig auto-dereferences for field access and then treat `*` in this construct as field name denoting the entire object. Ada does the same exact thing, except there you write `p.all` instead. In any case, while the exact syntax may not be ideal, using a postfix operator for dereferencing is vastly better than prefix in practice due to typical patterns of use. There's a reason why you end up writing () a lot in C code with heavy pointer operations - the things they end up mixed with most often turn out to have the wrong kind of priority and/or precedence more often than not. Things are much simpler when everything is postfix and code reads naturally left-to-right.
- IshKebab 2y ago
- 3836293648 2y agoPostfix is only superior when you have lots of things to chain. C doesn't have any chaining at all. f(g(x)) is just as good as f(g(x))
- card_zero 2y agoGotta escape those asterisks so you don't output italics. f(*g(*x)) is just as good as f(g(x*)*) Hmm, function names go before parentheses, that looks like a good reason for dereferencing to be prefix too, otherwise they end up a long way apart. But in the back of mind I'm thinking "semantics cause pointless fussy arguments, all code is ugly, let's stop programming in text somehow".
- 3836293648 2y agoOh yeah, forgot HN supported some markdown. But this is literally not semantics. Semantics is everything of value, this is just syntax.
- card_zero 2y agoSorry, yes, syntax. I was equating it to "purely semantic argument", arguing about the meaning of words. Here about it's where to put symbols.
- dfawcus 2y agoOne is able to chain calls in C via: x()->g()->f(); At least one person at Bell Labs historically used that scheme for some graphics program. All it requires is that the function returns a pointer to a struct which itself contains function pointers. C also allows the dot form if you really want it: x().g().f() Simply by returning structs, and for all compilers that I know if, such end up using a hidden pointers to structs. Now to make it a bit more usable, one needs a bit of planning so that either of these can be done: x(&x_out).g(&g_out, x_out).f(&f_out, g_out); x(&x_out)->g(&g_out, x_out)->f(&f_out, g_out); (I'd suggest the latter is now the more readable of the two). Where there is a VFT in each of the xxx_out structures, and the calls simply returns the VFT, while the whole abstraction is stored/returned via the out arguments.
- throwawaymaths 2y agooh no. this is DEFINITELY a place where zig made a GOOD change away from c. when a value is dereferenced, having a consistent left to right dereferencing makes sense. for example, in c, without looking up the order of operations how confident are you that you know what is going on in the following: foo = **bar[10]
- wat10000 2y agoIf prefix * bugs you, C has a suffix deference operator as well, `[0]`. (Yes, C programmers will probably hunt you down if you do this, but it does work.)
- throwawaymaths 2y agothis is not just a preference but a practical matter, esp. for reading nd checking other People's code. did you solve the puzzle at the end with high confidence?
- wat10000 2y agoYou mean `foo = *bar[10]`? That’s equivalent to `bar[10][0][0]`, i.e. the array element access is done first, then the retrieved element is dereferenced twice. I’m quite confident in this, but I’m a systems programmer working in C++, so that’s my bread and butter.
- deleted 2y ago[deleted]
- throwawaymaths 2y agoit's hard to disambiguate from bar[0][0][10]...
- wat10000 2y agoSure, I see your point and suffix does seem like the better option. But for people who do a lot of C, it’s not an issue.
- sudahtigabulan 2y agoIt's not just different. This syntax is less arbitrary than C's. It draws a syntactic parallel between accessing a single member and accessing "all members". (by using pattern-matching-like syntax) It makes the language more consistent and one's mental model of it smaller. (Even though I doubt that patterns other than the Kleene star would work) A parallel with files: cp dir/a dir/b dir/c /other/dir In Zig you can cp dir/* /other/dir In C: cp *dir /other/dir
- spacedcowboy 2y agoI don't actually agree that it makes the mental model smaller. I see something that is just different from what I expect, is cognitively jarring, interrupts the flow of what I'm doing, and forces me to focus on something I shouldn't need to. But it seems like I'm in the minority, so maybe it's just "old man shouts at clouds", I'll just ignore the language and move on :)