!$? operators for gratuitous overloading

We have all shared in the observation that all but the most carefully considered operator overloading tends to reduce code readability. There also many programmers that feel that they need it to make there code terse (or perhaps shiny) enough. I propose adding ! and $ as (normally unimplemented) binary operators and ? as a unary operator so as to explicitly to give people the rope they want but clearly tagged "here there be shenanigans". kcr

On 2020-02-02 15:00, Karl Ramm wrote:
In the past there have been suggestions to add None-coalescing operators such as "?." and "??", comparable to the null-coalescing operators of C#, so I'm -1 on just adding "!", "$" and "?" without a solid use-case, in case we find a better use for them later in the future.

On Sun, Feb 2, 2020, at 11:41, MRAB wrote:
Before the nameof discussion got shut down, I was going to propose $nameof as a way to avoid collision with a function called "nameof", and I still think it's viable as a general way to make an open-ended set of keywords that don't collide with identifiers [frozenset literals?]... so I'm also -1 on cutting down the number of reserved ASCII characters without a very good concrete use case. If we are going to have a general binary operator mechanism, maybe it should be something more haskell-like, with an open-ended set of binary operator names [`identifier` as haskell, perhaps? and/or maybe the unicode math ops section, with stuff like circled plus] and a way to define precedence before the point of use for the parser. Perhaps it would also be useful to provide a utility function for performing the __op__/__rop__/NotImplemented/most-derived-class binary operator execution rules on an arbitrary pair of method names, without *any* syntactical support [in order to, say, allow evaluation of custom ASTs for non-python languages on python objects]

On Feb 3, 2020, at 21:14, Random832 <random832@fastmail.com> wrote:
If we are going to have a general binary operator mechanism, maybe it should be something more haskell-like,
That’s exactly what I suggested—but more to argue that we don’t want a general binary operator mechanism at all. (The right way to do it would be like Haskell, we don’t want anything like whet Haskell does, therefore we shouldn’t do it.)
with an open-ended set of binary operator names [`identifier` as haskell, perhaps? and/or maybe the unicode math ops section, with stuff like circled plus] and a way to define precedence before the point of use for the parser.
Well, in Haskell, it’s any _string_ of symbol characters, not just any one symbol character. And I think you’d want that even if you open it up to all of Unicode Sm. Not just because most of the non-ASCII characters are hard to type in most environments, but because for an awful lot of operations there’s no obvious single character. For example, what Unicode character would be as good as >> for rshift even to read, much less to type? How could you distinguish |<, <|, |>, >|, and the other map insert/update operators by using single characters for each? And so on. And operator strings don’t work in Python, where things like 2+-3 are perfectly valid sequences of two operators (and 2-=3 is valid and not even an operator). Also, without sectioning, it would be hard to refer to operators—Python has the operator module for all the builtin ones, and operator.add looks just as nice as (+), but that doesn’t help if you need a Python equivalent to (|<>) or a non-ASCII character. Even the backticked names thing, while I don’t think it has any syntactic ambiguity, I don’t think it would be readable in Python, because you don’t read Python in terms of curried functions. For example, in Haskell, if I write odd `dropwhile` xs, that makes sense, because you think of it as the partially applied function (dropwhile odd) being applied to xs, but in Python nobody would think of it as partial(dropwhile, odd)(xs). So it would at best look backward and at worst incomprehensible. But anyway, even if there weren’t any problems with either one, I think the odds of convincing the Python community to add it would be pretty close to nil. That being said, somewhere or other I have a hacky import hook that I could dig up if you want it that lets you do something like this. (Hacky not just in the way that it’s implemented, but also in what it does and doesn’t allow—basically an operator is any string of Sm characters that happens to generate a single ERRORTOKEN when tokenized as normal Python, IIRC.) I can’t imagine using it in real life code, but it was fun to implement.
Perhaps it would also be useful to provide a utility function for performing the __op__/__rop__/NotImplemented/most-derived-class binary operator execution rules on an arbitrary pair of method names, without *any* syntactical support [in order to, say, allow evaluation of custom ASTs for non-python languages on python objects]
That part is not too hard to write, and doesn’t change from version to version, and if we don’t add custom operators (or encourage people to do so with import hooks) I think it will come up very rarely. So I think it’s probably not needed in the stdlib. (If you’ve got an idea for the opposite problem, though, a tool to make it easier to implement __op__ and __rop__ pairs on your types without having to go through the whole mess seen in fractions.Fraction, that would be pretty handy in the stdlib…)

Why should ? be a prefix operator and the other two infix? Especially given that those aren’t their syntax in other common languages (! is usually prefix, ? is usually suffix or infix or part of a ternary infix…): What precedence do they have? What associativity? Do the infix ones follow __rop__ protocols like arithmetic operators, different rules like comparisons, or no reverse overloading? Why these three and not backticks or tildes or Unicode symbols? I think in order to actually be useful for people who want this flexibility all of that may need to be configurable. At which point you might as well go full Haskell and allow people to define any string of symbol characters as a new operator. Of course that’s hard to do in Python, both because of the question of where to define things early enough and globally enough, and because Python doesn’t require spaces around operators so there’s nothing stopping someone from creating ambiguous operators… But I think the fact that code written with custom operators wouldn’t feel much like Python is a better argument against it than the practical difficulties.

Andrew Barnert <abarnert@yahoo.com> writes:
I figured there should be at least one unary operator, and I wanted the one character that essentially never is.
Since I was assuming they'd act like arithmetic operators, one of the binary operators being high priority and the other being low. No strong feelings as to which one, because the details are sort of beside the point.
Why these three and not backticks or tildes or Unicode symbols?
Because ~ is already bitwise inversion, I have a more interesting idea for backtick, and I do think all of our operators should be consistently typable on a standard keyboard layout.
I think in order to actually be useful for people who want this flexibility all of that may need to be configurable.
This is more rope for people who will go tie themselves up with it and leave the rest of us alone; it's not actually necessary for anything.
At which point you might as well go full Haskell and allow people to define any string of symbol characters as a new operator.
I do think that the people who actually use these should actually go write haskell instead.
But I think the fact that code written with custom operators wouldn’t feel much like Python is a
Arbitrary operators, in which case agreed (if I want Haskell I know where to find it) or just the leftover punctuation, in which case I _disagree_ because these would mark code as explicitly weird. I've mostly been dragging this idea around in my head for a while and wanted to get it out and see if anyone else was caught by it; it does seem to be going over like a lead ballon. kcr

On 2020-02-02 15:00, Karl Ramm wrote:
In the past there have been suggestions to add None-coalescing operators such as "?." and "??", comparable to the null-coalescing operators of C#, so I'm -1 on just adding "!", "$" and "?" without a solid use-case, in case we find a better use for them later in the future.

On Sun, Feb 2, 2020, at 11:41, MRAB wrote:
Before the nameof discussion got shut down, I was going to propose $nameof as a way to avoid collision with a function called "nameof", and I still think it's viable as a general way to make an open-ended set of keywords that don't collide with identifiers [frozenset literals?]... so I'm also -1 on cutting down the number of reserved ASCII characters without a very good concrete use case. If we are going to have a general binary operator mechanism, maybe it should be something more haskell-like, with an open-ended set of binary operator names [`identifier` as haskell, perhaps? and/or maybe the unicode math ops section, with stuff like circled plus] and a way to define precedence before the point of use for the parser. Perhaps it would also be useful to provide a utility function for performing the __op__/__rop__/NotImplemented/most-derived-class binary operator execution rules on an arbitrary pair of method names, without *any* syntactical support [in order to, say, allow evaluation of custom ASTs for non-python languages on python objects]

On Feb 3, 2020, at 21:14, Random832 <random832@fastmail.com> wrote:
If we are going to have a general binary operator mechanism, maybe it should be something more haskell-like,
That’s exactly what I suggested—but more to argue that we don’t want a general binary operator mechanism at all. (The right way to do it would be like Haskell, we don’t want anything like whet Haskell does, therefore we shouldn’t do it.)
with an open-ended set of binary operator names [`identifier` as haskell, perhaps? and/or maybe the unicode math ops section, with stuff like circled plus] and a way to define precedence before the point of use for the parser.
Well, in Haskell, it’s any _string_ of symbol characters, not just any one symbol character. And I think you’d want that even if you open it up to all of Unicode Sm. Not just because most of the non-ASCII characters are hard to type in most environments, but because for an awful lot of operations there’s no obvious single character. For example, what Unicode character would be as good as >> for rshift even to read, much less to type? How could you distinguish |<, <|, |>, >|, and the other map insert/update operators by using single characters for each? And so on. And operator strings don’t work in Python, where things like 2+-3 are perfectly valid sequences of two operators (and 2-=3 is valid and not even an operator). Also, without sectioning, it would be hard to refer to operators—Python has the operator module for all the builtin ones, and operator.add looks just as nice as (+), but that doesn’t help if you need a Python equivalent to (|<>) or a non-ASCII character. Even the backticked names thing, while I don’t think it has any syntactic ambiguity, I don’t think it would be readable in Python, because you don’t read Python in terms of curried functions. For example, in Haskell, if I write odd `dropwhile` xs, that makes sense, because you think of it as the partially applied function (dropwhile odd) being applied to xs, but in Python nobody would think of it as partial(dropwhile, odd)(xs). So it would at best look backward and at worst incomprehensible. But anyway, even if there weren’t any problems with either one, I think the odds of convincing the Python community to add it would be pretty close to nil. That being said, somewhere or other I have a hacky import hook that I could dig up if you want it that lets you do something like this. (Hacky not just in the way that it’s implemented, but also in what it does and doesn’t allow—basically an operator is any string of Sm characters that happens to generate a single ERRORTOKEN when tokenized as normal Python, IIRC.) I can’t imagine using it in real life code, but it was fun to implement.
Perhaps it would also be useful to provide a utility function for performing the __op__/__rop__/NotImplemented/most-derived-class binary operator execution rules on an arbitrary pair of method names, without *any* syntactical support [in order to, say, allow evaluation of custom ASTs for non-python languages on python objects]
That part is not too hard to write, and doesn’t change from version to version, and if we don’t add custom operators (or encourage people to do so with import hooks) I think it will come up very rarely. So I think it’s probably not needed in the stdlib. (If you’ve got an idea for the opposite problem, though, a tool to make it easier to implement __op__ and __rop__ pairs on your types without having to go through the whole mess seen in fractions.Fraction, that would be pretty handy in the stdlib…)

Why should ? be a prefix operator and the other two infix? Especially given that those aren’t their syntax in other common languages (! is usually prefix, ? is usually suffix or infix or part of a ternary infix…): What precedence do they have? What associativity? Do the infix ones follow __rop__ protocols like arithmetic operators, different rules like comparisons, or no reverse overloading? Why these three and not backticks or tildes or Unicode symbols? I think in order to actually be useful for people who want this flexibility all of that may need to be configurable. At which point you might as well go full Haskell and allow people to define any string of symbol characters as a new operator. Of course that’s hard to do in Python, both because of the question of where to define things early enough and globally enough, and because Python doesn’t require spaces around operators so there’s nothing stopping someone from creating ambiguous operators… But I think the fact that code written with custom operators wouldn’t feel much like Python is a better argument against it than the practical difficulties.

Andrew Barnert <abarnert@yahoo.com> writes:
I figured there should be at least one unary operator, and I wanted the one character that essentially never is.
Since I was assuming they'd act like arithmetic operators, one of the binary operators being high priority and the other being low. No strong feelings as to which one, because the details are sort of beside the point.
Why these three and not backticks or tildes or Unicode symbols?
Because ~ is already bitwise inversion, I have a more interesting idea for backtick, and I do think all of our operators should be consistently typable on a standard keyboard layout.
I think in order to actually be useful for people who want this flexibility all of that may need to be configurable.
This is more rope for people who will go tie themselves up with it and leave the rest of us alone; it's not actually necessary for anything.
At which point you might as well go full Haskell and allow people to define any string of symbol characters as a new operator.
I do think that the people who actually use these should actually go write haskell instead.
But I think the fact that code written with custom operators wouldn’t feel much like Python is a
Arbitrary operators, in which case agreed (if I want Haskell I know where to find it) or just the leftover punctuation, in which case I _disagree_ because these would mark code as explicitly weird. I've mostly been dragging this idea around in my head for a while and wanted to get it out and see if anyone else was caught by it; it does seem to be going over like a lead ballon. kcr
participants (4)
-
Andrew Barnert
-
Karl Ramm
-
MRAB
-
Random832