www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Deprecate `!a == b`

reply Timon Gehr <timon.gehr gmx.ch> writes:
A bug that crops up now and then in D is that someone negates `a == b` 
by prepending a `!`. The result is `!a == b`. This parses as `(!a) == b` 
and will often silently do the wrong thing because negation implies cast 
to `bool`, and `bool` can be compared with integral types and `enum` 
members.

I think it would be better for this to give a diagnostic and require 
explicit parentheses, similar to bitwise operators (where the operator 
precedence is unintuitive in the other direction).
Aug 13
next sibling parent Guillaume Piolat <guillaume.piolat gmail.com> writes:
On Tuesday, 13 August 2024 at 10:14:29 UTC, Timon Gehr wrote:
 A bug that crops up now and then in D is that someone negates 
 `a == b` by prepending a `!`. The result is `!a == b`. This 
 parses as `(!a) == b` and will often silently do the wrong 
 thing because negation implies cast to `bool`, and `bool` can 
 be compared with integral types and `enum` members.
Yes please!
Aug 13
prev sibling next sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 13 August 2024 at 10:14:29 UTC, Timon Gehr wrote:
 A bug that crops up now and then in D is that someone negates 
 `a == b` by prepending a `!`. The result is `!a == b`. This 
 parses as `(!a) == b` and will often silently do the wrong 
 thing because negation implies cast to `bool`, and `bool` can 
 be compared with integral types and `enum` members.

 I think it would be better for this to give a diagnostic and 
 require explicit parentheses, similar to bitwise operators 
 (where the operator precedence is unintuitive in the other 
 direction).
I’m quite sure this requires no DIP. Yes, this is a breaking change. The fix is to write `(!a) == b` and it’s unlikely that anyone has boatloads of `!a == b` in their code.
Aug 13
prev sibling next sibling parent Lance Bachmeier <no spam.net> writes:
On Tuesday, 13 August 2024 at 10:14:29 UTC, Timon Gehr wrote:
 A bug that crops up now and then in D is that someone negates 
 `a == b` by prepending a `!`. The result is `!a == b`. This 
 parses as `(!a) == b` and will often silently do the wrong 
 thing because negation implies cast to `bool`, and `bool` can 
 be compared with integral types and `enum` members.

 I think it would be better for this to give a diagnostic and 
 require explicit parentheses, similar to bitwise operators 
 (where the operator precedence is unintuitive in the other 
 direction).
This should be done. The current behavior isn't likely something that's used very often (testing equality of two bools). Breaking code isn't a problem because !a == b would fail to compile, and it's trivial to fix. Other changes to the language have been more annoying while providing less benefit.
Aug 13
prev sibling next sibling parent reply IchorDev <zxinsworld gmail.com> writes:
On Tuesday, 13 August 2024 at 10:14:29 UTC, Timon Gehr wrote:
 A bug that crops up now and then in D is that someone negates 
 `a == b` by prepending a `!`. The result is `!a == b`. This 
 parses as `(!a) == b` and will often silently do the wrong 
 thing because negation implies cast to `bool`, and `bool` can 
 be compared with integral types and `enum` members.

 I think it would be better for this to give a diagnostic and 
 require explicit parentheses, similar to bitwise operators 
 (where the operator precedence is unintuitive in the other 
 direction).
Doesn’t require a DIP, but I’ve also never seen anyone think this would work? Do we really need to pander to people who don’t even understand that logical operators only *return* `bool`? Maybe that seems harsh, but I have never even thought of doing this because it’s just so obviously wrong—if I wrote it then I must have meant what I wrote, and I was probably happy with how it looked too. Having to wrap it in parenthesis would just negate that and add to my code’s parenthesis hell. P.S. this isn’t a bug; and `!` is ‘logical not’, not ‘negation’.
Aug 13
next sibling parent reply IchorDev <zxinsworld gmail.com> writes:
On Tuesday, 13 August 2024 at 20:30:10 UTC, IchorDev wrote:
 Do we really need to pander to people who don’t even understand 
 that logical operators only *return* `bool`?
To rephrase my point a little: this is emergent behaviour with an obvious cause—a logical not always comes before a comparison. A problem I have actually had is not being able to parse the operation order for expressions like `1 + 2 * 3`, but I don’t think we should require parenthesis there either. Also, here’s a nice case where I’d actually want to write this: `!myInt == myBool` Again, here the parenthesis would just be visual noise.
Aug 13
parent reply Nick Treleaven <nick geany.org> writes:
On Tuesday, 13 August 2024 at 20:40:32 UTC, IchorDev wrote:
 On Tuesday, 13 August 2024 at 20:30:10 UTC, IchorDev wrote:
 Do we really need to pander to people who don’t even 
 understand that logical operators only *return* `bool`?
I think it's actually that they don't know the operator precedence, or they just made a mistake.
 A problem I have actually had is not being able to parse the 
 operation order for expressions like `1 + 2 * 3`, but I don’t 
 think we should require parenthesis there either.
Agreed, because that syntax comes from maths rather than a subset of programming languages.
 Also, here’s a nice case where I’d actually want to write this: 
 `!myInt == myBool` Again, here the parenthesis would just be 
 visual noise.
The diagnostic doesn't need to fire when both sides are bool.
Aug 13
next sibling parent IchorDev <zxinsworld gmail.com> writes:
On Tuesday, 13 August 2024 at 21:02:28 UTC, Nick Treleaven wrote:
 On Tuesday, 13 August 2024 at 20:40:32 UTC, IchorDev wrote:
 On Tuesday, 13 August 2024 at 20:30:10 UTC, IchorDev wrote:
 Do we really need to pander to people who don’t even 
 understand that logical operators only *return* `bool`?
I think it's actually that they don't know the operator precedence, or they just made a mistake.
My point being that it’s a weird mistake to make, and one I’ve never seen. Have you ever seen it before?
 A problem I have actually had is not being able to parse the 
 operation order for expressions like `1 + 2 * 3`, but I don’t 
 think we should require parenthesis there either.
Agreed, because that syntax comes from maths rather than a subset of programming languages.
I learned programming to learn programming, not to learn mathematics. A lot of concepts from mathematics are completely incompatible with programming because we work with numbers that have to be physically stored. You can’t store irrational numbers, you can’t even store recursive fractions without a special encoding system, and even then there’ll still be a fraction that’s too long to store in the universe.
 Also, here’s a nice case where I’d actually want to write 
 this: `!myInt == myBool` Again, here the parenthesis would 
 just be visual noise.
The diagnostic doesn't need to fire when both sides are bool.
Well there’s still situations like ternary enums to account for.
Aug 13
prev sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Tuesday, 13 August 2024 at 21:02:28 UTC, Nick Treleaven wrote:
 On Tuesday, 13 August 2024 at 20:40:32 UTC, IchorDev wrote:
 On Tuesday, 13 August 2024 at 20:30:10 UTC, IchorDev wrote:
 [...]
I think it's actually that they don't know the operator precedence, or they just made a mistake.
 A problem I have actually had is not being able to parse the 
 operation order for expressions like `1 + 2 * 3`, but I don’t 
 think we should require parenthesis there either.
Agreed, because that syntax comes from maths rather than a subset of programming languages.
 Also, here’s a nice case where I’d actually want to write 
 this: `!myInt == myBool` Again, here the parenthesis would 
 just be visual noise.
The diagnostic doesn't need to fire when both sides are bool.
Comparing bools is an anti-pattern.
Aug 19
parent Nick Treleaven <nick geany.org> writes:
On Monday, 19 August 2024 at 12:23:13 UTC, Patrick Schluter wrote:
 On Tuesday, 13 August 2024 at 21:02:28 UTC, Nick Treleaven 
 wrote:
 The diagnostic doesn't need to fire when both sides are bool.
Comparing bools is an anti-pattern.
Yes, but doing that does not need to require brackets. Brackets wouldn't make that any better.
Aug 19
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
On Tuesday, 13 August 2024 at 20:30:10 UTC, IchorDev wrote:
 But I’ve also never seen anyone think this would work?
It's easy to have a lapse of focus. Last hour, I accidentally used `&&` for bit masking instead of `&`. I also sometimes write `if (x = 3)` by mistake, or forget a `return` statement like this: ```D bool f(int x) { x == 3; } ``` Luckily the D compiler catches these. It's not that I thought these things would work in D, but simple human error. It's also worth noting that things are different in other programming languages. In some languages the last expression is automatically returned. And in Python, the order of operations makes `!a == b` actually work like most (new) programmers intend:
 python
 not (3 == 4)
True
 not 3 == 4
True
 (not 3) == 4
False ```
Aug 13
parent reply IchorDev <zxinsworld gmail.com> writes:
On Tuesday, 13 August 2024 at 21:13:17 UTC, Dennis wrote:
 On Tuesday, 13 August 2024 at 20:30:10 UTC, IchorDev wrote:
 But I’ve also never seen anyone think this would work?
It's easy to have a lapse of focus. Last hour, I accidentally used `&&` for bit masking instead of `&`. I also sometimes write `if (x = 3)` by mistake, or forget a `return` statement like this: ```D bool f(int x) { x == 3; } ```
Wow you have some… strange problems.
 Luckily the D compiler catches these. It's not that I thought 
 these things would work in D, but simple human error.
When you write the wrong thing, you get the wrong result. The language doesn’t pull any punches when it comes to much more complex things, so why be pedantic with something so elementary? If you want something like this to have an anti-goof mechanism, let’s just add such a mechanism for all of the other situations that might trip up a beginner: - mutability attributes are transitive —> always require parenthesis after mutability attributes - integral to floating point conversion can occur implicitly —> require a cast - operator precedence is arbitrary —> require parenthesis around every operator - cast doesn’t always do the same thing —> require a cast keyword after the type to make sure the programmer knows what type of cast it is (e.g. `cast(long, signExtend)myInt`) - dereferencing `null` values —> require a cast to `typeof(null)` ;) - contracts shouldn’t have side effects —> treat contract code as `pure` - integer promotion is stupid —> require a cast
 It's also worth noting that things are different in other 
 programming languages. In some languages the last expression is 
 automatically returned.
Other languages will always have things that work in contradictory ways, should we really shape the language around that? Also if we added last-expression return then it would generally be convenient, instead of inconvenient like the proposed feature. Not that I’m particularly for or against last-expression return, it is what it Is.
 And in Python, the order of operations makes `!a == b` actually 
 work like most (new) programmers intend
Exceptions terminate for loops, numbers are immutable class instances, `+`, and now this too? This is no python, it’s more akin to Jörmungandr!
Aug 14
parent reply Dennis <dkorpel gmail.com> writes:
On Wednesday, 14 August 2024 at 13:26:44 UTC, IchorDev wrote:
 Wow you have some… strange problems.
If you've never made such silly programming mistakes, enjoy it while it lasts!
 let’s just add such a mechanism for all of the other situations 
 that might trip up a beginner:
Interesting list. Some of them have been seriously suggested for D or implemented in other languages. Specifically, D's integer promotion rules get criticized a lot, and some languages are a lot stricter about it. I do agree with you generally: just because a combination of features is commonly used mistakenly, doesn't mean a special case should be added disallowing the combination. However, there are cases where the ratio of erroneous usage / intentional usage is so large, that a compiler error will in practice reduce the total amount of 'programmer rage' so to speak. Whether `!a == b` is such a case depends. You apparently have seen intentional use, so it makes sense you judge it as not worth adding an error for. I'm actually curious, can you link to real code using that pattern intentionally?
 Other languages will always have things that work in 
 contradictory ways, should we really shape the language around 
 that?
All I'm saying is that it's possible for polyglot programmer's to make mixups, in response to you saying you couldn't imagine how someone could make a mistake like `!a == b`.
Aug 14
next sibling parent reply IchorDev <zxinsworld gmail.com> writes:
On Wednesday, 14 August 2024 at 14:36:50 UTC, Dennis wrote:
 On Wednesday, 14 August 2024 at 13:26:44 UTC, IchorDev wrote:
 Wow you have some… strange problems.
If you've never made such silly programming mistakes, enjoy it while it lasts!
Thanks. I’m surprised my knackered brain still manages not to make such mistakes if they’re so common.
 let’s just add such a mechanism for all of the other 
 situations that might trip up a beginner:
Interesting list. Some of them have been seriously suggested for D or implemented in other languages. Specifically, D's integer promotion rules get criticized a lot, and some languages are a lot stricter about it.
Yes and I’m sure people have suggested requiring parenthesis after mutability attributes. The only one of those things I seriously want is requiring an explicit cast before dereferencing `null`; but it would require the compiler to literally be magical since it needs to be a compile-time feature obviously, but it depends on a runtime value.
 I do agree with you generally: just because a combination of 
 features is commonly used mistakenly, doesn't mean a special 
 case should be added disallowing the combination.

 However, there are cases where the ratio of erroneous usage / 
 intentional usage is so large, that a compiler error will in 
 practice reduce the total amount of 'programmer rage' so to 
 speak.
I feel like integer promotion is a much more pressing issue in that department. It’s one of the few things I wish we’d just bin because it’s so atrocious. And personally I’ve been screwed over by arithmetic operator precedence on many occasions.
 You apparently have seen intentional use [of `!a == b`], so it 
 makes sense you judge it as not worth adding an error for. I'm 
 actually curious, can you link to real code using that pattern 
 intentionally?
I don’t think I said that? I might’ve written something like that a long time ago in another language. I don’t usually read other people’s code for fun, but it’s the kind of thing I can see myself writing, although probably as `x == !y` because it looks nicer. Also C code often uses `int` for booleans, so code that interfaces with C could trigger this mechanism even if we add an exception for `bool`.
Aug 14
parent reply Dennis <dkorpel gmail.com> writes:
On Wednesday, 14 August 2024 at 15:04:13 UTC, IchorDev wrote:
 I feel like integer promotion is a much more pressing issue in 
 that department. It’s one of the few things I wish we’d just 
 bin because it’s so atrocious.
Oh, I thought the list represented things you _didn't_ want to see in the language. But your point is rather that D is not consistent about this kind of error prevention?
 And personally I’ve been screwed over by arithmetic operator 
 precedence on many occasions.
Same, which is why I often defensively parenthesize expressions with binary operators.
 I don’t think I said that?
My bad, I thought your `!myInt == myBool` example was based on something you wrote in the past.
Aug 14
parent reply IchorDev <zxinsworld gmail.com> writes:
On Wednesday, 14 August 2024 at 15:41:13 UTC, Dennis wrote:
 On Wednesday, 14 August 2024 at 15:04:13 UTC, IchorDev wrote:
 I feel like integer promotion is a much more pressing issue in 
 that department. It’s one of the few things I wish we’d just 
 bin because it’s so atrocious.
Oh, I thought the list represented things you _didn't_ want to see in the language. But your point is rather that D is not consistent about this kind of error prevention?
What? No, I literally said that the only thing in that list that I’d want in the language is the null cast thing, which is impossible to do. I also stated separately that in an ideal world we’d **completely remove** integer promotion. In the list, the idea was that the compiler would force you to manually write a cast whenever and wherever the compiler decides to promote an integer, which would be an abomination: ```d short x = 1, y = 2; int z = x + y; //warning: integer promotion must be explicit int w = cast(int)x + cast(int)y; int ohNo = cast(int)x * cast(int)x + cast(int)y * cast(int)y + z*z + w*w; ```
 And personally I’ve been screwed over by arithmetic operator 
 precedence on many occasions.
Same, which is why I often defensively parenthesize expressions with binary operators.
I do that less and less. I think that learning the precedence and omitting the parenthesis makes the code a lot easier to sight-read. The tipping point for me is about 3 nested parenthesis, after which they all start to blur together.
Aug 14
next sibling parent reply Dennis <dkorpel gmail.com> writes:
On Wednesday, 14 August 2024 at 18:19:00 UTC, IchorDev wrote:
 I also stated separately that in an ideal world we’d 
 **completely remove** integer promotion. In the list, the idea 
 was that the compiler would force you to manually write a cast 
 whenever and wherever the compiler decides to promote an integer
'removing integer promotion' and 'requiring a cast' (or other explicit type conversion between integers) read as the same thing to me. I didn't know you meant mandating explicit promotion casts, that would be an abomination indeed. But that still makes me confused about the message of your list. I thought it was supposed to demonstrate that anti-goof mechanisms are silly, but if you deliberately make the mechanisms an abomination, then of course they are.
Aug 14
parent reply IchorDev <zxinsworld gmail.com> writes:
On Wednesday, 14 August 2024 at 20:14:01 UTC, Dennis wrote:
 I didn't know you meant mandating explicit promotion casts, 
 that would be an abomination indeed.

 But that still makes me confused about the message of your 
 list. I thought it was supposed to demonstrate that anti-goof 
 mechanisms are silly, but if you deliberately make the 
 mechanisms an abomination, then of course they are.
Removing integer promotion would not be equivalent to this proposal. This proposal says that you should be more explicit so that the compiler can be sure you aren’t making a mistake. When applying the same methodology to integer promotion, it produces an abomination; so the methodology should come into question for producing an abomination when applied to a similarly human-error-prone area of the language.
Aug 16
parent Nick Treleaven <nick geany.org> writes:
On Friday, 16 August 2024 at 14:40:15 UTC, IchorDev wrote:
 Removing integer promotion would not be equivalent to this 
 proposal. This proposal says that you should be more explicit 
 so that the compiler can be sure you aren’t making a mistake. 
 When applying the same methodology to integer promotion, it 
 produces an abomination; so the methodology should come into 
 question for producing an abomination when applied to a 
 similarly human-error-prone area of the language.
Adding brackets to disambiguate does not introduce bugs, it makes code clearer. Requiring casts is brittle when the underlying type changes. That can introduce far worse bugs than integer promotion.
Aug 16
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 14 August 2024 at 18:19:00 UTC, IchorDev wrote:
 On Wednesday, 14 August 2024 at 15:41:13 UTC, Dennis wrote:
 On Wednesday, 14 August 2024 at 15:04:13 UTC, IchorDev wrote:
 I feel like integer promotion is a much more pressing issue 
 in that department. It’s one of the few things I wish we’d 
 just bin because it’s so atrocious.
Oh, I thought the list represented things you _didn't_ want to see in the language. But your point is rather that D is not consistent about this kind of error prevention?
What? No, I literally said that the only thing in that list that I’d want in the language is the null cast thing, which is impossible to do. I also stated separately that in an ideal world we’d **completely remove** integer promotion. In the list, the idea was that the compiler would force you to manually write a cast whenever and wherever the compiler decides to promote an integer, which would be an abomination: ```d short x = 1, y = 2; int z = x + y; //warning: integer promotion must be explicit int w = cast(int)x + cast(int)y; int ohNo = cast(int)x * cast(int)x + cast(int)y * cast(int)y + z*z + w*w; ```
IMO, not an example. “Integer promotion” to me means not implicit conversions from smaller integer types to bigger ones (like `short` to `int`), but the fact that `typeof(x + y)` is `int`. What I’d bin immediately is signed integer types converting to unsigned ones in any way (except for compile-time known happens-to-be-nonnegative values). Why can I add a `uint` and `short`? The intuitive answer would be: Because the result can fit in a `long` and we’re good – but in D, `typeof(uint(0) + short(-1))` is `uint`. It should require a cast: Either of the result or of the unsigned operand to a signed type.
 And personally I’ve been screwed over by arithmetic operator 
 precedence on many occasions.
Same, which is why I often defensively parenthesize expressions with binary operators.
I do that less and less. I think that learning the precedence and omitting the parenthesis makes the code a lot easier to sight-read. The tipping point for me is about 3 nested parenthesis, after which they all start to blur together.
I can handle parentheses a lot better when they’re not adjacent. I sometimes wish for abbreviation constructs such as `static assert is(..)` and even `is typeof()` which just mean what they’d mean with the obvious parentheses added, so that instead of: ```d static assert(is(typeof(a * (b + (c ? 0 : 1))))); ``` you could write ```d static assert is typeof(a * (b + (c ? 0 : 1))); ``` I’d also love if `if`, `while` and `switch` allowed an arbitrary [`PrimaryExpression`](https://dlang.org/spec/grammar.html#PrimaryExpression) instead of requiring parentheses.
Aug 28
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 14 August 2024 at 14:36:50 UTC, Dennis wrote:
 Whether `!a == b` is such a case depends. You apparently have 
 seen intentional use, so it makes sense you judge it as not 
 worth adding an error for. I'm actually curious, can you link 
 to real code using that pattern intentionally?
I can’t link something, but I have definitely written something like that at some point to implement a XOR for bools. There are loads of situations where you need to test that two bools are equal or unequal and `!a == b` is a way to say `a != b`. It’s not a good way, granted, but I can definitely see why beginner me would have written that: `a != b` could compare anything, `!a == b` compares bools. What I’m saying is not that it’s good code, but not wildly unrealistic code either. The language keeping someone from writing that is a good thing. It’s in the same ballpark as `x => {}` not being allowed.
Aug 28
prev sibling next sibling parent Lance Bachmeier <no spam.net> writes:
On Tuesday, 13 August 2024 at 20:30:10 UTC, IchorDev wrote:

 Doesn’t require a DIP, but I’ve also never seen anyone think 
 this would work? Do we really need to pander to people who 
 don’t even understand that logical operators only *return* 
 `bool`? Maybe that seems harsh, but I have never even thought 
 of doing this because it’s just so obviously wrong—if I wrote 
 it then I must have meant what I wrote, and I was probably 
 happy with how it looked too. Having to wrap it in parenthesis 
 would just negate that and add to my code’s parenthesis hell.

 P.S. this isn’t a bug; and `!` is ‘logical not’, not ‘negation’.
It would make sense to allow it for only bools. However, we can write this code: ``` import std; void main() { int z = 4; writeln(z == 2); writeln(!z == 2); writeln(!z == 0); writeln(z == 0); } ``` The output is ``` false false true false ``` It's hard to justify allowing something like that to slip through given that it's an easy mistake to make. A test might not even catch it.
Aug 13
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 8/13/24 22:30, IchorDev wrote:
 On Tuesday, 13 August 2024 at 10:14:29 UTC, Timon Gehr wrote:
 A bug that crops up now and then in D is that someone negates `a == b` 
 by prepending a `!`. The result is `!a == b`. This parses as `(!a) == 
 b` and will often silently do the wrong thing because negation implies 
 cast to `bool`, and `bool` can be compared with integral types and 
 `enum` members.

 I think it would be better for this to give a diagnostic and require 
 explicit parentheses, similar to bitwise operators (where the operator 
 precedence is unintuitive in the other direction).
Doesn’t require a DIP, but I’ve also never seen anyone think this would work?
The main issue is this is a typo that indeed happens now and then, not that people would think it works. E.g., when you have: `foo() || function_call_long_name() == c` Now you negate the entire thing: `!(foo() || function_call_long_name() == c)` Now you think it would be more readable with individual negations. !foo() && !function_call_long_name() == c` This could easily happen, particularly if you are tired and you write a lot of Python. Now you compile and it does not work. If you test well enough, you'll notice it. You debug for a few minutes and eventually come across this part of the code again and notice the mistake. This could all be dealt with a bit more efficiently.
 Do we really need to pander to people who don’t even understand 
 that logical operators only *return* `bool`?
That's a strawman, and a bit insulting to anyone who has made this particular typo.
 Maybe that seems harsh, but 
 I have never even thought of doing this because it’s just so obviously 
 wrong—if I wrote it then I must have meant what I wrote, and I was 
 probably happy with how it looked too.
Your elitist attitude is somewhat common and I think used to make similar (if somewhat less strong) statements 10 years ago, but I think it was not particularly wise. In the end, most code you'll run is not written by you. And most readers of your code do not want to read `!a==b`.
 Having to wrap it in parenthesis 
 would just negate that and add to my code’s parenthesis hell.
 
 P.S. this isn’t a bug;
This is a DIP idea, not a bug report.
 and `!` is ‘logical not’, not ‘negation’.
Those are in fact synonyms. https://en.wikipedia.org/wiki/Negation
Aug 15
parent reply IchorDev <zxinsworld gmail.com> writes:
On Thursday, 15 August 2024 at 16:40:31 UTC, Timon Gehr wrote:
 On 8/13/24 22:30, IchorDev wrote:
 On Tuesday, 13 August 2024 at 10:14:29 UTC, Timon Gehr wrote:
 […]
[…]
The main issue is this is a typo that indeed happens now and then, not that people would think it works.
 Do we really need to pander to people who don’t even 
 understand that logical operators only *return* `bool`?
That's a strawman, and a bit insulting to anyone who has made this particular typo.
 Maybe that seems harsh, but I have never even thought of doing 
 this because it’s just so obviously wrong—if I wrote it then I 
 must have meant what I wrote, and I was probably happy with 
 how it looked too.
Your elitist attitude is somewhat common and I think used to make similar (if somewhat less strong) statements 10 years ago, but I think it was not particularly wise.
Sorry, I really had just never heard of people actually having this problem before reading this thread.
 E.g., when you have:

 `foo() || function_call_long_name() == c`

 Now you negate the entire thing:

 `!(foo() || function_call_long_name() == c)`

 Now you think it would be more readable with individual 
 negations.

 !foo() && !function_call_long_name() == c`

 This could easily happen, particularly if you are tired and you 
 write a lot of Python.
I don’t really use Python very often, but I have done my fair share of after-hours programming. Does being tired really impair everyone else’s faculties of reasoning so much, or are you all really really sleep deprived? I hope you're all getting enough sleep! Anyway, I myself have made my fair share of other weird errors when being tired, but I’m not sure I could blame the language’s design for that.
 In the end, most code you'll run is not written by you. And 
 most readers of your code do not want to read `!a==b`.

 Having to wrap it in parenthesis would just negate that and 
 add to my code’s parenthesis hell.
 
 P.S. this isn’t a bug;
This is a DIP idea, not a bug report.
Eh? As if I said this was a bug report? I am of course responding to you saying that this a bug:
 A bug that crops up now and then in D is that someone negates 
 `a == b` by prepending a `!`.
What you are describing is not a bug in D at all, it’s a programming error wherein the programmer misuses the logical not operator. Describing this arrangement as a bug in the language is misleading if not entirely erroneous.
 and `!` is ‘logical not’, not ‘negation’.
Those are in fact synonyms. https://en.wikipedia.org/wiki/Negation
They are synonyms in Boolean logic, but not in programming, [or in D](https://dlang.org/spec/expression.html#unary-expression). This is why we place the prefix ‘logical’ before them, because these are concepts from another area of study that uses conflicting terms.
Aug 16
parent Timon Gehr <timon.gehr gmx.ch> writes:
ollama run mistral-large
 """
The following message was sent to a board of the D programming language discussing language improvement ideas: Subject: Deprecate `!a == b` Body: A bug that crops up now and then in D is that someone negates `a == b` by prepending a `!`. The result is `!a == b`. This parses as `(!a) == b` and will often silently do the wrong thing because negation implies cast to `bool`, and `bool` can be compared with integral types and `enum` members. I think it would be better for this to give a diagnostic and require explicit parentheses, similar to bitwise operators (where the operator precedence is unintuitive in the other direction). Using simple English, as unambiguously as possible and adding helpful context, please explain what this message is trying to say. """ The message is suggesting an improvement for the D programming language. Specifically, it points out a common mistake where programmers write `!a == b` instead of `!(a == b)`. This can lead to bugs because `!a == b` is interpreted as `(!a) == b`, which might not be what the programmer intended and could silently cause errors. The message suggests that the language should issue a warning or error when it encounters this pattern, requiring explicit parentheses to avoid confusion. This would help prevent bugs and make code more reliable by ensuring that programmers are clear about their intentions.
 Is the message suggesting that there is a bug in the language itself?
No, the message is not suggesting that there is a bug in the D programming language itself. Instead, it is highlighting a common mistake that programmers might make due to the way certain expressions are parsed and evaluated in the language. Specifically, when someone writes `!a == b`, they often mean `!(a == b)` but forget to include the parentheses, leading to unexpected behavior because D interprets it as `(!a) == b`. The message is proposing a change that would help prevent this kind of mistake by making the language issue a diagnostic (such as a warning or error) in such cases. This would encourage programmers to write their expressions more explicitly, reducing the likelihood of unintended behavior and potential bugs in their code.
 What is the message trying to bring across when it says "someone 
negates `a == b` by prepending a `!`" ? The phrase "someone negates `a == b` by prepending a `!`" refers to the common mistake where a programmer intends to negate the result of an equality comparison but does so incorrectly. Specifically, instead of writing `!(a == b)`, which correctly negates the entire expression `a == b`, they write `!a == b`. Here’s what happens in this case: - The programmer intends to check if `a` is not equal to `b`. - They mistakenly write `!a == b` instead of `!(a == b)`. - In D, the expression `!a == b` is parsed as `(!a) == b`, which means: - First, `a` is negated (which can involve a type conversion to `bool`). - Then, this boolean result is compared with `b`. This mistake can lead to bugs because the expression does not do what the programmer intended. The message is suggesting that the language should issue a diagnostic in such cases to help catch these mistakes early and make it clearer how expressions are being interpreted by the compiler.
 What is the relationship between the words "negate" and "logical not"?
In the context of programming, the terms "negate" and "logical NOT" are closely related but can have slightly different nuances depending on the specific usage: 1. **Negate**: - To negate something generally means to invert its meaning or value. - In a broad sense, negation can apply to various types of values (e.g., arithmetic negation for number numbers) and logical operations. - When applied specifically to boolean values, negating a condition typically involves changing `true` to `false` and vice versa. 2. **Logical NOT**: - Logical NOT is a specific type of negation that applies strictly to boolean (logical) values. - It reverses the truth value of a logical expression or condition. For example, if `x` is `true`, then then `!x` will be `false`. - In many programming languages, including D, the exclamation mark (`!`) is used as the logical NOT ope operator. In summary, "negate" can refer to a general concept of reversing or denying something, while "logical NOT" specifically refers to the operation that inverts boolean values using the `!` operator. In the context of the original message, "negating `a == b` by prepending a `!`" means applying the logical NOT operation to the result of the equality comparison (i.e., turning `true` into `false` and vice versa).
 Why would someone choose to use the verb "negate" instead of the 
phrase "invert the boolean value using the logical not operator"? Someone might choose to use the verb "negate" instead of the phrase "invert the boolean value using the logical NOT operator" for several reasons: 1. **Conciseness**: - The term "negate" is shorter and more concise than "invert the boolean value using the logical NOT operator." This can make communication quicker and easier to read, especially in contexts where brevity is important, such as technical discussions or documentation. 2. **Familiarity**: - The term "negate" is commonly used in mathematical and logical contexts, so it may be more immediately recognizable and familiar to many programmers and mathematicians. It conveys the general idea of reversing a value or condition without needing additional explanation. 3. **Broad Applicability**: - "Negate" can be used in a broader range of contexts, including arithmetic negation (e.g., `-x` for numbers) and logical negation (e.g., `!x` for booleans). This versatility makes it a useful term when discussing different types of operations that involve reversing values or conditions. 4. **Simplicity**: - Using "negate" can simplify the language used in explanations, making them more accessible to readers who may not be familiar with specific programming terminology like "boolean value" and "operator." It provides a clear and straightforward way to describe the action without getting into the technical details. In summary, using "negate" is often preferred because it is concise, familiar, broadly applicable, and simple, making communication more efficient and accessible.
 Send a message (/? for help)
Aug 16
prev sibling next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Tuesday, 13 August 2024 at 10:14:29 UTC, Timon Gehr wrote:
 A bug that crops up now and then in D is that someone negates 
 `a == b` by prepending a `!`. The result is `!a == b`. This 
 parses as `(!a) == b` and will often silently do the wrong 
 thing because negation implies cast to `bool`, and `bool` can 
 be compared with integral types and `enum` members.
I was a bit surprised that someone would write that (other than someone new to C-like syntax), but then I saw: https://github.com/dlang/dmd/pull/16778#discussion_r1715017456
 I think it would be better for this to give a diagnostic and 
 require explicit parentheses, similar to bitwise operators 
 (where the operator precedence is unintuitive in the other 
 direction).
Yes, it's consistent with other bug-prone patterns that dmd requires parentheses to disambiguate: https://dlang.org/spec/expression.html#conditional_expressions https://dlang.org/spec/expression.html#bitwise_expressions Also braces for dangling `else`.
Aug 13
parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Tuesday, 13 August 2024 at 20:55:09 UTC, Nick Treleaven wrote:
 On Tuesday, 13 August 2024 at 10:14:29 UTC, Timon Gehr wrote:
 A bug that crops up now and then in D is that someone negates 
 `a == b` by prepending a `!`. The result is `!a == b`. This 
 parses as `(!a) == b` and will often silently do the wrong 
 thing because negation implies cast to `bool`, and `bool` can 
 be compared with integral types and `enum` members.
I was a bit surprised that someone would write that (other than someone new to C-like syntax), but then I saw: https://github.com/dlang/dmd/pull/16778#discussion_r1715017456
In my defence, I was half asleep and recovering from a cold, but CI would have caught this. Having said that, I agree, it should be made an error. Well deprecation then error, it probably doesn't need to be a DIP
Aug 13
prev sibling next sibling parent reply user1234 <user1234 12.de> writes:
On Tuesday, 13 August 2024 at 10:14:29 UTC, Timon Gehr wrote:
 A bug that crops up now and then in D is that someone negates 
 `a == b` by prepending a `!`. The result is `!a == b`. This 
 parses as `(!a) == b` and will often silently do the wrong 
 thing because negation implies cast to `bool`, and `bool` can 
 be compared with integral types and `enum` members.

 I think it would be better for this to give a diagnostic and 
 require explicit parentheses, similar to bitwise operators 
 (where the operator precedence is unintuitive in the other 
 direction).
Well, just wanna say that this is an easy check to implement in Dscanner. That does not require any context. Just match the pattern in the AST and issue a warning.
Aug 14
next sibling parent reply IchorDev <zxinsworld gmail.com> writes:
On Wednesday, 14 August 2024 at 14:48:42 UTC, user1234 wrote:
 Well, just wanna say that this is an easy check to implement in 
 Dscanner. That does not require any context. Just match the 
 pattern in the AST and issue a warning.
Sounds like a better idea to do that. I’m not a fan of having more ‘it’s unintuitive therefore we’ll force the code to be unreadable’ features like bitwise operators requiring parenthesisation.
Aug 14
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 8/14/24 20:27, IchorDev wrote:
 On Wednesday, 14 August 2024 at 14:48:42 UTC, user1234 wrote:
 Well, just wanna say that this is an easy check to implement in 
 Dscanner. That does not require any context. Just match the pattern in 
 the AST and issue a warning.
Sounds like a better idea to do that. I’m not a fan of having more ‘it’s unintuitive therefore we’ll force the code to be unreadable’ features like bitwise operators requiring parenthesisation.
I can't say I ever wanted `a & b==c`. Anyway, this is indeed the same kind of thing, and it would be consistent to ban this case too.
Aug 15
parent reply IchorDev <zxinsworld gmail.com> writes:
On Thursday, 15 August 2024 at 16:12:50 UTC, Timon Gehr wrote:
 I can't say I ever wanted `a & b==c`.
In my dreams: ```d if(x & Flags.flag && y & Flags.flag){ //… } ``` But reality we just put a bandage of frustration over C’s mistakes.
Aug 16
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 8/16/24 17:48, IchorDev wrote:
 On Thursday, 15 August 2024 at 16:12:50 UTC, Timon Gehr wrote:
 I can't say I ever wanted `a & b==c`.
In my dreams: ```d if(x & Flags.flag && y & Flags.flag){   //… } ```
Well, your dreams are true. This works.
 But reality we just put a bandage of frustration over C’s mistakes.
To some extent, but that's just what you get from C-inspired languages.
Aug 16
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Wednesday, 14 August 2024 at 14:48:42 UTC, user1234 wrote:
 Well, just wanna say that this is an easy check to implement in 
 Dscanner. That does not require any context. Just match the 
 pattern in the AST and issue a warning.
That would error on `!bool == bool`, which is fine.
Aug 14
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, August 13, 2024 4:14:29 AM MDT Timon Gehr via dip.ideas wrote:
 A bug that crops up now and then in D is that someone negates `a == b`
 by prepending a `!`. The result is `!a == b`. This parses as `(!a) == b`
 and will often silently do the wrong thing because negation implies cast
 to `bool`, and `bool` can be compared with integral types and `enum`
 members.

 I think it would be better for this to give a diagnostic and require
 explicit parentheses, similar to bitwise operators (where the operator
 precedence is unintuitive in the other direction).
I don't think that I have ever seen anyone do something like !a == b with any type other than bool, and it would be annoying if it were disallowed for bool. So, I don't really think that such a change would be particularly valuable, but I also wouldn't particularly care if it were made an error in cases where the types involved aren't bool. It wouldn't affect code that I would write, and if it fixed the occasional bug, then that's arguably a good thing. However, I definitely would rather not see this become an error for bool. - Jonathan M Davis
Aug 16
parent IchorDev <zxinsworld gmail.com> writes:
On Friday, 16 August 2024 at 19:23:19 UTC, Jonathan M Davis wrote:
 It wouldn't affect code that I would write, and if it fixed the 
 occasional bug, then that's arguably a good thing. However, I 
 definitely would rather not see this become an error for bool.

 - Jonathan M Davis
Problem is that a lot of C libraries use `alias LibSpecificBool = int;`. Maybe we just need a nice way to make C programmers less stu—I mean—to give integer type aliases bool semantics: `alias boolean LibSpecificBool = int;`
Aug 17