digitalmars.dip.ideas - User-defined operator symbols
- Quirin Schroll (22/22) Feb 09 We can have user-defined operators in D without burdening the
- Richard (Rikki) Andrew Cattermole (25/49) Feb 09 Identifiers are very much not like this at all.
- Quirin Schroll (17/34) Feb 17 What I meant by “There shouldn’t be any confusion about them” is
- Richard (Rikki) Andrew Cattermole (2/19) Feb 17 https://www.unicode.org/reports/tr25/
- Timon Gehr (10/21) Feb 09 I think user-defined binary operators just should not have any default
- Quirin Schroll (25/49) Feb 17 True, everyone in the mathematical-leaning pro-custom-operator
- Timon Gehr (59/113) Feb 18 If that is a concern then just know that he is opposed to everything
- Dukc (4/7) Feb 09 Why you're confident they'll be never used as identifiers, if you
We can have user-defined operators in D without burdening the parser and programmers reading the code too much, make it predictable, and useful. * User-defined binary operators get a new precedence between `+` and `*`. They’re always left-associative, and there’s no way to change that. * User-defined unary operators would be _defined_ prefix-only (i.e. no postfix user-defined operators) and have the same precedence as all other prefix operators. * User-defined compound-assignment operators would have the same precedence as all the other compound-assignment operators and, like them, be right-associative. Any custom operators would have to be (single) Unicode symbols outside of ASCII, optionally followed by `=` for compound-assignment, e.g. `∘` and `×=`. Those symbols will never be added to D with some built-in meaning and neither be used in identifiers, so we’d definitely not close any doors. Like regular overloadable operators, they’re implemented with `opBinary`, `opUnary`, and `opOpAssign` templates. Like identifier names, those custom operators have domain-specific (usually mathematical) meaning. There shouldn’t be any confusion about them.
Feb 09
On 10/02/2026 12:30 AM, Quirin Schroll wrote:We can have user-defined operators in D without burdening the parser and programmers reading the code too much, make it predictable, and useful. * User-defined binary operators get a new precedence between `+` and `*`. They’re always left-associative, and there’s no way to change that. * User-defined unary operators would be _defined_ prefix-only (i.e. no postfix user-defined operators) and have the same precedence as all other prefix operators. * User-defined compound-assignment operators would have the same precedence as all the other compound-assignment operators and, like them, be right-associative. Any custom operators would have to be (single) Unicode symbols outside of ASCII, optionally followed by `=` for compound-assignment, e.g. `∘` and `×=`. Those symbols will never be added to D with some built-in meaning and neither be used in identifiers, so we’d definitely not close any doors. Like regular overloadable operators, they’re implemented with `opBinary`, `opUnary`, and `opOpAssign` templates. Like identifier names, those custom operators have domain-specific (usually mathematical) meaning. There shouldn’t be any confusion about them.Identifiers are very much not like this at all. UAX31 is nearly 25 years old, it has had significant amount of attention put to it, and is adopted by many mainstream languages. The characters are determined based upon their category, not "someone wanted it". It is very well studied. TR25 on the other hand, its table hasn't been updated since 2014, in progress a major revision as of last year. The characters are hand picked, some by block, some based upon opinion. It has not been adopted in any language that I know of. 1. TR25 is not mature enough to consider for inclusion into D. Currently I am concerned it exhibits some of the worst bits of C standard pre 23's identifiers. It needs a lot more time to cook into a functioning specification. 2. Based upon previous interactions with Walter I am confident to say that creating our own tables will not be up for consideration. 3. Having the parser see a binary/unary/binary|unary operator and then spit out a AST node that'll get lowered to opBinary/opUnary/opBinaryRight/opOpAssign is acceptable. 4. D is not a mathematical language, there needs to be assessment for what D is missing to become one, or the benefit of this won't manifest. 5. Precedence would probably be the lowest one and they will all have it. I can't see how it could work any other way. There is no table for this. As of right now, it does not cross into worth my time to implement. I considered it a few years ago, the situation has yet to change.
Feb 09
On Monday, 9 February 2026 at 12:08:38 UTC, Richard (Rikki) Andrew Cattermole wrote:On 10/02/2026 12:30 AM, Quirin Schroll wrote:What I meant by “There shouldn’t be any confusion about them” is that someone reading the code is unlikely not to know what an operator means. For example, if you read `a × b`, that `×` operator can mean many different things in mathematics, but when you encounter one, it’s usually perfectly clear what the operator stands for. That is very similar to how identifier names are usually words that have real-world meaning and if a word has many different meanings, it’s usually clear from the overall context what specific meaning is referred to. What and how to choose which Unicode symbols should be allowed as custom operators in D is up to debate. This is a DIP idea, after all, not a fleshed out proposal draft.Like identifier names, those custom operators have domain-specific (usually mathematical) meaning. There shouldn’t be any confusion about them.Identifiers are very much not like this at all.UAX31 is nearly 25 years old, it has had significant amount of attention put to it, and is adopted by many mainstream languages. The characters are determined based upon their category, not "someone wanted it". It is very well studied. TR25 on the other hand, its table hasn't been updated since 2014, in progress a major revision as of last year. The characters are hand picked, some by block, some based upon opinion. It has not been adopted in any language that I know of. 1. TR25 is not mature enough to consider for inclusion into D. Currently I am concerned it exhibits some of the worst bits of C standard pre 23's identifiers. It needs a lot more time to cook into a functioning specification.What is TR25? Googling it, I can’t find anything seemingly relevant, just products and Pokémon.
Feb 17
On 18/02/2026 12:23 AM, Quirin Schroll wrote:
UAX31 is nearly 25 years old, it has had significant amount of
attention put to it, and is adopted by many mainstream languages.
The characters are determined based upon their category, not
"someone wanted it". It is very well studied.
TR25 on the other hand, its table hasn't been updated since 2014, in
progress a major revision as of last year. The characters are hand
picked, some by block, some based upon opinion. It has not been
adopted in any language that I know of.
1. TR25 is not mature enough to consider for inclusion into D.
Currently I am concerned it exhibits some of the worst bits of C
standard pre 23's identifiers. It needs a lot more time to cook
into a functioning specification.
What is TR25? Googling it, I can’t find anything seemingly relevant,
just products and Pokémon.
https://www.unicode.org/reports/tr25/
Feb 17
On 2/9/26 12:30, Quirin Schroll wrote:We can have user-defined operators in D without burdening the parser and programmers reading the code too much, make it predictable, and useful. * User-defined binary operators get a new precedence between `+` and `*`. They’re always left-associative, and there’s no way to change that.I think user-defined binary operators just should not have any default precedence or associativity. By not giving a default precedence to user-defined binary operators, you don't paint yourself into a corner. Also, even with customizable precedence and associativity, you don't have to burden the parser at all nor make it depend on semantic, you can just defer precedence/associativity resolution to semantic. Just parse the expression with ambiguous precedence/associativity, this is not very hard to do.* User-defined unary operators would be _defined_ prefix-only (i.e. no postfix user-defined operators) and have the same precedence as all other prefix operators. * User-defined compound-assignment operators would have the same precedence as all the other compound-assignment operators and, like them, be right-associative.Fair.
Feb 09
On Monday, 9 February 2026 at 15:59:41 UTC, Timon Gehr wrote:On 2/9/26 12:30, Quirin Schroll wrote:True, everyone in the mathematical-leaning pro-custom-operator camp wants that. For obvious reasons. There are just tradeoffs involved.We can have user-defined operators in D without burdening the parser and programmers reading the code too much, make it predictable, and useful. * User-defined binary operators get a new precedence between `+` and `*`. They’re always left-associative, and there’s no way to change that.I think user-defined binary operators just should not have any default precedence or associativity. By not giving a default precedence to user-defined binary operators, you don't paint yourself into a corner.Also, even with customizable precedence and associativity, you don't have to burden the parser at all nor make it depend on semantic, you can just defer precedence/associativity resolution to semantic. Just parse the expression with ambiguous precedence/associativity, this is not very hard to do.I’ve heard that Walter is opposed to that. Maybe it’s old news, maybe it’s wrong entirely. What’s definitely true is that D cannot advertise having a context-free grammar if parsing allowed for that kind of ambiguity.If we allow for customizable precedence and associativity, we can also allow custom postfix operators which get lowered to `opUnaryRight` or something. The reason I originally avoided them is to make parsing easy: If you have `a ∘ × ⋅ b` it’s clear that it must be `a ∘ (×(⋅b))`. The rule was just the most natural tie-breaker rule given how D’s current operators work. If we defer associativity and precedence, `a ∘ × ⋅ b` can also end up meaning `(a∘) × (⋅b)`. The reason I picked “a new precedence between addition and multiplication” for user-defined operators is because that’s the most likely one you’d be fine with if it has to be a uniform approach. Maybe it’s better to say: Any user-defined binary operator binds stronger than `&&` and weaker than unary prefix operators―and that’s it. Like `a == b & c`, it’s considered ambiguous and thus an error; you need clarifying parentheses. If you had `a + b × c`, you’d have to write `a + (b × c)` or `(a + b) × c`.* User-defined unary operators would be _defined_ prefix-only (i.e. no postfix user-defined operators) and have the same precedence as all other prefix operators. * User-defined compound-assignment operators would have the same precedence as all the other compound-assignment operators and, like them, be right-associative.Fair.
Feb 17
On 2/17/26 12:43, Quirin Schroll wrote:On Monday, 9 February 2026 at 15:59:41 UTC, Timon Gehr wrote:If that is a concern then just know that he is opposed to everything around expanding operator overloading, so anything in this thread is DOA anyway and this is just a purely philosophical discussion in the first place.On 2/9/26 12:30, Quirin Schroll wrote:True, everyone in the mathematical-leaning pro-custom-operator camp wants that. For obvious reasons. There are just tradeoffs involved.We can have user-defined operators in D without burdening the parser and programmers reading the code too much, make it predictable, and useful. * User-defined binary operators get a new precedence between `+` and `*`. They’re always left-associative, and there’s no way to change that.I think user-defined binary operators just should not have any default precedence or associativity. By not giving a default precedence to user-defined binary operators, you don't paint yourself into a corner.Also, even with customizable precedence and associativity, you don't have to burden the parser at all nor make it depend on semantic, you can just defer precedence/associativity resolution to semantic. Just parse the expression with ambiguous precedence/associativity, this is not very hard to do.I’ve heard that Walter is opposed to that.Maybe it’s old news, maybe it’s wrong entirely. What’s definitely true is that D cannot advertise having a context-free grammar if parsing allowed for that kind of ambiguity. ...No, that's definitely false. The grammar being context-free has nothing to do with whether operator precedence is resolved during semantic or in the parser. D is not a context-free language, the point about the context-free grammar specifically applies to what the parser is doing, you can do whatever you want in semantic: ```d import std; int mul(int a, int b)=>a*b; int add(int a, int b)=>a+b; struct Run{ template opDispatch(string op){ static string parse(string[] ops){ int i=0; string parseVar(){ return text("x[",i++,"]"); } string parseMul(ref string[] ops){ auto r=parseVar(); while(ops.length&&ops[0]=="mul"){ ops.popFront(); r=text("mul(",r,",",parseVar(),")"); } return r; } string parseAdd(ref string[] ops){ auto r=parseMul(ops); while(ops.length&&ops[0]=="add"){ ops.popFront(); r=text("add(",r,",",parseMul(ops),")"); } return r; } return parseAdd(ops); } static opDispatch(T...)(T x){ pragma(msg,parse(op.split("_"))); return mixin(parse(op.split("_"))); } } } void main(){ assert(Run.mul_add_mul(1,2,3,4)==1*2+3*4); assert(Run.add_mul_add(1,2,3,4)==1+2*3+4); assert(Run.mul_mul_add_add(1,2,3,4,5)==1*2*3+4+5); assert(Run.add_add_mul_mul(1,2,3,4,5)==1+2+3*4*5); } ``` What is important is that the parser can build a parse tree without having to rely on semantic information. Resolving operator precedence during semantic analysis does not threaten that, operator precedence just would no longer be encoded into the grammar.Not really. I prefer not having custom operators over having custom operators with an arbitrarily chosen precedence and associativity.If we allow for customizable precedence and associativity, we can also allow custom postfix operators which get lowered to `opUnaryRight` or something. The reason I originally avoided them is to make parsing easy: If you have `a ∘ × ⋅ b` it’s clear that it must be `a ∘ (×(⋅b))`. The rule was just the most natural tie-breaker rule given how D’s current operators work. If we defer associativity and precedence, `a ∘ × ⋅ b` can also end up meaning `(a∘) × (⋅b)`. The reason I picked “a new precedence between addition and multiplication” for user-defined operators is because that’s the most likely one you’d be fine with if it has to be a uniform approach. ...* User-defined unary operators would be _defined_ prefix-only (i.e. no postfix user-defined operators) and have the same precedence as all other prefix operators. * User-defined compound-assignment operators would have the same precedence as all the other compound-assignment operators and, like them, be right-associative.Fair.Maybe it’s better to say: Any user-defined binary operator binds stronger than `&&` and weaker than unary prefix operators―and that’s it. Like `a == b & c`, it’s considered ambiguous and thus an error; you need clarifying parentheses. If you had `a + b × c`, you’d have to write `a + (b × c)` or `(a + b) × c`.That's certainly better, though it's still arbitrary.
Feb 18
On Monday, 9 February 2026 at 11:30:14 UTC, Quirin Schroll wrote:Those symbols will never be added to D with some built-in meaning and neither be used in identifiers, so we’d definitely not close any doors.Why you're confident they'll be never used as identifiers, if you still hope the maintainers might be happy to include them as operators?
Feb 09









"Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> 