| ▲ | Partial Operator Precedence(ziglang.org) |
| 59 points by tosh 3 days ago | 30 comments |
| |
|
| ▲ | ozgrakkurt 2 hours ago | parent | next [-] |
| Here is a crazy idea, don’t use operators, just use functions. Operators cause issues because it is not possible to cleanly express error output from an operator, it is not possible to express the bit-width of the operation, have to understand precedence. All of these issues cause other downstream issues. Why not add_u64()? |
| |
| ▲ | anonymous908213 2 hours ago | parent | next [-] | | Because it's ridiculously verbose. Abbreviating extremely common operations makes code far easier to read and understand than not. > have to understand precedence Precedence is not in any way an argument against using operators; you could disallow precedence at all (eg. each expression must have at most one operator, or rather two counting the assignment operator) while still having operators. That said, precedence is also very beneficial to readability when not abused. > Operators cause issues because it is not possible to cleanly express error output from an operator Incorrect. An operator can simply desugar to functions, and therefore has the exact same error semantic possibilities. If you take issue with a language not returning errors from operators, that is a language design issue, not something inherent to the existence of operators. | | |
| ▲ | epolanski 2 hours ago | parent [-] | | The amount of times I've been sad that multiply or sum are longer than + or * is limited to long formulas and even then it's a non issue. Also, functions can be used as callbacks, and offer overloads to handle partial applications with ease, pipe(2, sum(3), multiply(2)), e.g. | | |
| ▲ | OskarS an hour ago | parent | next [-] | | I don't think your experience is particularly universal, it vastly differs depending on what kind of programming you do. If you're doing games stuff or graphics or signal processing or any other number of subfields of programming that require a lot of math, operators are a god-send for readability. This Zig change is the best of both worlds: avoid the ambiguity, but keep the readability. | |
| ▲ | vidarh an hour ago | parent | prev | next [-] | | There are plenty of languages, like e.g. Ruby, where operators are just sugar for functions or methods. Meaning they can still be partially applied, have their value taken, and/or passed as callbacks. If your language doesn't allow you to do the same thing with operators as you can do with functions, that's a language design issue and has nothing to do with operators as a concept. | |
| ▲ | flohofwoe an hour ago | parent | prev | next [-] | | > The amount of times I've been sad that multiply or sum are longer than + or * is limited to long formulas and even then it's a non issue. It depends on what things you work on. When I'm writing 'math-y' code (GPU shaders for renderng etc) then I want the expressions to look close to the reference paper and not artificially split them up into multiple lines of code with function calls instead of operators. | |
| ▲ | bennettnate5 an hour ago | parent | prev | next [-] | | I don't think character length of an expression is the issue. Reading chained operators is explicitly easier than reading nested functions by virtue of operators having infix notation and not explicitly requiring parantheses where they are not necessary to disambiguate order of operations. The prefix notation of functions moves the operation farther from the eye from operands and introduces the issue of parantheses-miscounting bugs. I'd hardly say the difference in readability of these is a non issue: (a + b + c + d) * e mult(add(a, add(b, add(c, d))), e) | | |
| ▲ | Mawr an hour ago | parent | next [-] | | > (a + b + c + d) * e a
|> add(b)
|> add(c)
|> add(d)
|> mult(e)
> mult(add(a, add(b, add(c, d))), e) c
|> add(d)
|> add(b, _)
|> add(a, _)
|> mult(e)
| |
| ▲ | Zambyte an hour ago | parent | prev | next [-] | | mult(add(a, b, c, d), e) :) | |
| ▲ | epolanski an hour ago | parent | prev [-] | | Hence I said that operators are more readable for longer formulas. |
| |
| ▲ | anonymous908213 2 hours ago | parent | prev [-] | | Operators can also support those things. Operators are just syntax sugar for functions, or at least they're capable of being such. If a language chooses not to support the full range of what functions can do for operators, that is a thing you can criticise the language for (and I do!), but it is not a reason to suggest operators should be disposed with in language design altogether. |
|
| |
| ▲ | shiandow an hour ago | parent | prev | next [-] | | Personally I think the meaning of + and * should be context dependent. Though the main reason for infix notation is to signal associativity. Which a lot of these don't satisfy. A lot of these are just trying to mimic natural language, which can be useful but as lisp demonstrates is not that important if the grammar is clear enough. | |
| ▲ | solatic 2 hours ago | parent | prev | next [-] | | Zig sometimes gives you this, for example there are @divCeil, @divTrunc, @divExact, and @divFloor built-ins so that you can be more precise in your intent compared to using "/". It's rather simple to then write a linting rule that fails if you ever have a literal "/" in your codebase. | |
| ▲ | flohofwoe an hour ago | parent | prev [-] | | ...and while at why not also dump the assignment and indexing operators! assign(result, add(b, mul(c, sub(d, e)), add(add(f, g), array_get(arr, i)));
(I bet I messed up the parentheses somewhere).At that point, even assembly code is more readable. Also there's a reason why Lisp and Forth haven't exactly taken the world by storm ;) |
|
|
| ▲ | Luke2642 an hour ago | parent | prev | next [-] |
| Saturating vs wrapping operators are so nice! Apparently compiles to 3 or 4 branchless ops on most architectures. |
|
| ▲ | Someone 2 hours ago | parent | prev | next [-] |
| I don’t find the article clear on how it fixes things, but I guess that, now a op1 b op2 c
is an error if op1 is unequal to op2 and they have the same precedence. Do I see that right?Also, it seems each operator will have to have a associativity to handle a future operator for exponentiation or (also potential future) custom operators. Finally, the “What you need to know” section doesn’t mention orelse. Where does that fit in? |
| |
| ▲ | dan-robertson an hour ago | parent [-] | | The point is that there is no longer a global ‘precedence number’ where the operator with the higher precedence binds tighter. Instead, it is only valid to write your example for some combinations of op1 and op2; other combinations will require explicit parentheses. Presumably orelse is not in the table because it requires parentheses for any other operator. |
|
|
| ▲ | v9v 2 hours ago | parent | prev | next [-] |
| Something very similar was in Fortress, see 16.2 here https://www.ccs.neu.edu/home/samth/fortress-spec.pdf |
|
| ▲ | skrebbel 4 hours ago | parent | prev | next [-] |
| I never realized that languages have so many strict precedence levels because that’s easier to implement than not having them. This here seems like a very good idea and I hope other languages in active development take note. And als JS’ Prettier, which actively removes parentheses like these. |
| |
| ▲ | amenghra 9 minutes ago | parent | next [-] | | Things become error prone once you realize that the precedence rules are different across languages! | |
| ▲ | pasc1878 3 hours ago | parent | prev | next [-] | | No you can have no precedence at all see Lisp and APL for examples. Lisp because everything is explicit and only the form just inside an opening parenthesis does anything.
APL as all is scanned right to left. | |
| ▲ | qalmakka 2 hours ago | parent | prev [-] | | If you use the shunting yard algorithm or similar for infix binary operators it's trivial to define a table with operator precedence values and use that. You can have a bunch of operators all at the same priority but it is the same complexity really, the algorithm is pretty trivial |
|
|
| ▲ | raphlinus 4 hours ago | parent | prev | next [-] |
| Worth noting, WGSL does this too, and is mostly a bit stricter than Zig: `a + b & c` and `a << b & c` are errors in WGSL but accepted by Zig. (Quick: off the top of your head, how do those parse)? I'm thinking of doing this for symbolasm also. |
|
| ▲ | elegios 2 hours ago | parent | prev | next [-] |
| This is one of those ideas that is very weird if you’re approaching it from more or less traditional parsing techniques, but makes a lot of sense when you think about it. I ended up doing a bunch of work on it in a slightly broader and more formal setting during my phd (https://dl.acm.org/doi/abs/10.1145/3571251) with the expectation that it probably wouldn’t be relevant for more mainstream languages any time soon, very happy to be wrong on that. |
|
| ▲ | sheept 3 hours ago | parent | prev | next [-] |
| JavaScript does something similar for the exponentiation operator. This is a syntax error: -a ** b
Someone who learned PEMDAS in school may argue it should be equivalent to -(a * b), while a programmer may be used to unary operators binding tightest, so (-a) * b. |
|
| ▲ | Scaevolus 4 hours ago | parent | prev | next [-] |
| C/C++ obviously have far too many precedence levels, but you can easily have a more readable tower without forcing parentheses, like Go's 5 levels: https://go.dev/ref/spec#Operator_precedence |
|
| ▲ | IshKebab 3 hours ago | parent | prev | next [-] |
| This is very clearly the best design for operator precedence IMO and it's nice that Zig has converged on it. It would be good to get it as a linting/formatting rule in Rust too (maybe that already exists?). |
|
| ▲ | boxed 4 hours ago | parent | prev [-] |
| I like the compromise, although I still think operator precedence in itself is evil :P Lisp kinda made the right call by mistake. |
| |
| ▲ | jraph 3 hours ago | parent | next [-] | | Made the right call, yes, but at the right time and with the right parameters above all! | |
| ▲ | tgv 3 hours ago | parent | prev [-] | | So did Forth, but that's no guarantee for readability. APL got the worst of both worlds, IMO. |
|