Bug 24842 – No ability to overload unary logial not operator

Status
REOPENED
Severity
enhancement
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2024-11-03T02:04:48Z
Last change time
2024-12-13T19:38:25Z
Assigned to
No Owner
Creator
Mai Lapyst
Moved to GitHub: dmd#18279 →

Comments

Comment #0 by info — 2024-11-03T02:04:48Z
Dlang has wide range support for overloading nearly all operators the language supports. However, I'm missing support for the unary logical not operator. Usecases are implementation of (feature-)flags, or my current use-case, notation of if an certain format is allowed or not for serialization / processing. Essentially all datastuctures that are logically negateable would benefit of the ability to overload the logical not operator. Example: ```d struct FormatId { string id; bool allowed = true; this(string id) { this.id = id; } private this(string id, bool allowed) { this.id = id; this.allowed = allowed; } auto opUnary(string op)() if (op == "!") { return FormatId(this.id, !this.allowed); } } unittest { auto f1 = FormatId("test"); assert(f1.allowed == true); auto f2 = !f1; assert(f1.allowed == true); assert(f2.allowed == false); } ```
Comment #1 by dominikus — 2024-11-03T10:18:27Z
"!" is overwritten using the opCast!bool() operator (it's a cast to the other boolean value). This is like opCmp() overwrites all comparison operators.
Comment #2 by dominikus — 2024-11-03T10:27:42Z
See language reference 21.2.1 "Boolean Operations"
Comment #3 by info — 2024-11-04T22:29:53Z
> See language reference 21.2.1 "Boolean Operations" I'm well aware of this section of the specs, but it's not an viable substitude for what I'm propsing. It's also not anywhere near the ability of overloading the `!` operator, because this section uses a trick by simple converting it to a plain bool beforehand via `opCast!(bool)()`. But when using this, any other information is lost forever. Like in the example I send with the inital comment; it contains additional information which I cannot retrieve after the conversion to an plain boolean value; but I **need** to somehow keep it since the type **itself** is boolish (which means has an two-value state). Again, what I want is to create an struct `FormatId("abc", true)` which when negated using the d-native `!`-operator turns into an instance of `FormatId("abc", false)`. This is simply not possible at the moment. I can respect if this is not an usecase that dlang wishes to support, but I feel the language then would limit itself unnessearily.
Comment #4 by dominikus — 2024-11-04T23:37:50Z
This is the reason some operators are convoluted - to prevent someone from shooting himself in the feat by implementing them inconsistently. Everybody expects the ! operator to return a boolean, not some "boolish" object. This is because it is used in conditions, where nothing else then the logical value is needed. Any other information is not evaluated in a condition, so why keep it? If you want something different, write some other member function which returns something "not-ish".
Comment #5 by info — 2024-11-05T00:01:25Z
Thats hardly an reason imho. Because if we use that logic, everybody would expect `+` to only work on numerical values since it means "addition", jet it's perfectly fine to do things like this: ```d struct A {} struct B {} struct C { B opBinary(string op : "+")(A rhs){ return B(); } } void main() { C c; B b = c + A(); } ``` I would argue that this produces the same "shooting yourself in the foot" as an overloadable ! operator would.
Comment #6 by dominikus — 2024-11-05T11:49:36Z
(In reply to Mai Lapyst from comment #5) > [...] if we use that logic, everybody would > expect `+` to only work on numerical values since it means "addition" Yes, that would be desirable, but it's much harder to enforce, as it is not easy to detect if something is "numeric" or not. At least D has the "~" operator, so that is no more necessary to convolute addition and concatenation. I think D found a good compromise by only forbidding things that are confusing and at the same time really not necessary. If you need to "negate" some object, why not overload the unary operator "-" or "~"? They are much more commonly understood to produce some kind of inverse. If your usecase is only that you like to write "!" instead of something else, no matter how confusing that is for the reader of your code, I fear this will not convince anybody to add such a "feature".
Comment #7 by info — 2024-11-06T00:56:02Z
> Yes, that would be desirable, but it's much harder to enforce, as it is not easy to detect if something is "numeric" or not. At least D has the "~" operator, so that is no more necessary to convolute addition and concatenation. Sure it easy, after all, dlang has both the `std.traits.isNumeric` and `std.traits.isScalar` templates that can detect numerical / scalar values, which would be the only types one would expect `+` to work on anyway. > If you need to "negate" some object, why not overload the unary operator "-" or "~"? > ... > If your usecase is only that you like to write "!" instead of something else ... My usecase is that i find writing `-` (which is an arithmetic inverse) and `~` (which is both an bit-wise inverse AND an concatination in dlang), much more confusing to read when an boolish, logical negation is that what is wanted & being done in-code. I would also argue that it's more commonly understood that `!` means logical, boolish inverse across languages.
Comment #8 by robert.schadek — 2024-12-13T19:38:25Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/18279 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB