-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Go 2: add chained interval comparisons #33694
Comments
If people would just learn to write them like this:
or, for exclusion:
the benefits of this suggestion would be very small. |
I believe the rule here would be that |
@robpike While that looks enticing, keeping the variable on the left side is often the most common thing to do, and switching between having it on the left and right, especially in a single expression, makes me second/third guess myself on my "greater than"s and my "less than"s. I still am unsure if I like this proposal, while I've definitely thought "man, a language where you could chain comparisons would be cool" (I did not know python did this), I don't think it quite fits into Go. I can say that when chained even further (not sure this is really a use case), it definitely is easier to understand:
that may be hard to understand, but this is harder in my eyes:
Granted, complexity should grow vertically, not horizontally. I also could have possibly made it a bit clearer if I had done something like Either way, it seems like a nifty feature, but it's not a need-to-have |
I think it is best to allow chaining for monotone relations. x < y >= z is not what it looks like. It could be misread as an interval check. |
There is another advantage related to IEEE floats. Think of a typical function checking its parameters:
This kind of early return is good practice & very common. Now check this out:
This is the output:
NaN, the notorious design flaw in IEEE 754, passes the test O_o
which fixes the test:
Logically those are the same tests, but NaN breaks mathematical logic. This is why (among other reasons) it is a notorious design flaw! With chained interval checks, we comfortably write:
and everything works ;) |
I'm somewhat concerned about the short-circuiting behavior. In an expression like |
It is similar to |
It's different because |
Sorry, mobile phone. |
When I write the expression When I write the expression When I write the expression My point is simply that |
With Also |
There is also the issue of number of components allowed for chaining. These are the possibilities: A) Just 3 components like B) Up to 4 components like
These are much rarer as expected: examples from go:
5+ cases examples from kubernetes:
1+ cases Only 6+ cases in eleven Go code bases that I've checked. Possibly a couple dozens in all Go code publicly available. C) 5+ components. I saw one in Go, first example above. If this proposal would be accepted, my vote is for B because:
5+ component comparisons can still benefit a lot from chaining. I think this is the right balance. |
It's also worth noting that |
In the first case c must of an ordered type, in the second c must be bool. So say if you mistyped a <= as !=, it won't compile. Do you mean the compiler gets some extra difficulty distinguishing these cases? |
Also, currently, the comparison operators simply follow the rules for other binary operators, so we'd have to introduce an irregularity there. it's easy to apply De Morgan's laws with the current definition. If we allow I am not convinced this form of syntactic sugar - as appealing as it looks - is worth the cost. |
Hi Robert,
They are called monotone (non-)increasing / (non)-decreasing sequences of numbers. This is standard math terminology. So the following are not, for example, monotone relations:
I am updating the main proposal above to be more clear about monotonicity, and then I will have a follow up about negations. |
Hi again Robert, First, this proposal does not in any way intend to manipulate the fundamental laws of math, like DeMorgan's (they are set in stone but maybe we could propose something about QM ;P that is for another day).
This is a very typical example of early return. It is also good practice. Here the programmer wants to accept only finite x from It is not the programmer's fault (it is an archaic hw design flaw), but her responsibility, to take great care about this, unfortunately. What she should have written is:
which fixes the test. What we could all enjoy writing instead is:
There are two things here. The first one is monotone chaining which we already talked about. For the second see this:
This does not compile because Here are two examples from Go itself:
Here is how we could write them:
Personally I find the latter much more readable and clear. It expresses your intent much better. I rest my case ;) |
@jfcg I didn't mean to imply that your suggestion manipulates fundamental laws or math. What I said is that predicate negations using DeMorgan's rules become more complex and somewhat unintuitive. Regarding your example with NaNs: I don't think that is a convincing example. NaNs are a design flaw (I'd agree with that wholeheartedly), and I suspect almost no numeric code is correct in the presence of NaNs. It's best to avoid them. |
Quick question: if we adjust relative priority of |
Yes. That would be a silent change in the behavior of existing code. We can't do that. |
Yes, but I meant is there a specific case? |
Oh, sorry, now I see what you mean. I think you're right: I can't think of any way to use |
Just brainstorming :P
What do you think of the following operator precedence?
What I am curious about is if this could be non-breaking for existing compiling Go code. |
We've discussed this quite a bit, and it seems to us that this idea, while sometimes convenient, doesn't seem to meet the "importance" criteria of a language change ("address an important issue for many people"). We're also concerned that the novel short-circuiting behavior isn't a good fit with Go. We don't want to change the operator precedence levels, which are (we hope) simple enough to remember with only five levels (and changing them would likely not be backward compatible). For these reasons, this is a likely decline. Leaving open for a month for final comments. |
I agree with @robpike that
But there's another situation, somewhat related, but different. Don't know if anybody had problem with it, but I did. Here it is:
If I were designing a language on my own, I probably wouldn't bother to add "chained intervals", but I definitely would add this:
And having "or" instead of or a synonym to "||" would also help. |
@latitov Your example - as you say - is unrelated to this issue. That said, the specific example you're giving would probably be written as switch Somewhere.SomeVeryLongVariable {
case 1, 4, 9, 25:
... which is possible now and which is concise with no repetition. If the comparison is more complex, you can always introduce a temporary variable, which is one reason why we have initialization expressions in if t := Somewhere.SomeVeryLongVariable; t == 1 || t == 4 || t == 9 || t == 29 { ... I don't see any reason why expressions should be complicated to support your suggestion given that it reduces each sub-expression from It's easy to come up with arbitrary new syntax that simplifies a specific use case - but it's a slippery slope: typically it's not worth the extra complexity introduced into the language. |
@griesemer you are right. Specifically, you are right that this is slippery slope, and that it's unrelated issue. You are right there. However, you are not right about "some specific use-case". We all come from different backgrounds. Some work in biotech, others in finance, others in Google... Every field sometimes make what is "rare specific use-case", a common thing. For example, I develop industrial automation software, a programs that loop indefinitely 24/7/365, and control temperature, pressure, etc. In this particular field, the following is a common thing:
and switch/case won't work here. Well, you can nest switch/case inside |
There no further comments relevant to this issue, so closing. |
Python comparisons can be chained like:
This means:
This is intuitive and easier to read. This proposal covers monotone relations only (
<,<=,>,>=
in one direction) like:x < y <= z
a > b > c
It does not cover any non-monotone relations like:
p >= q < r
q < w != e
a != s == d
In order to determine places where such chained interval comparisons could be used in Go, we can use an (improved) bash script like:
On some popular projects developed in Go, we get the following examples & totals:
examples from dgraph:
448+ cases
examples from etcd:
276+ cases
examples from frp:
219+ cases
examples from gitea:
590+ cases
examples from go:
1369+ cases
examples from influxdb:
53+ cases
examples from kubernetes:
1524+ cases
examples from moby:
461+ cases
examples from nomad:
769+ cases
examples from prometheus:
550+ cases
examples from terraform:
653+ cases
As seen from 6912+ cases above, many thousands of if / case / for clauses doing interval checks can be made simpler and easier to read. Also, it has advantages for IEEE floats, see below.
What do you think?
The text was updated successfully, but these errors were encountered: