While we're discussing crazy ideas inspired by a combination of a long-abandoned PEP and Haskell idioms (see the implicit lambda thread), here's another: arbitrary infix operators: a `foo` b == foo(a, b) I'm not sure there's any use for this, I just have a nagging feeling there _might_ be, based on thinking about how Haskell uses them to avoid the need for special syntax in a lot of cases where Python can't. This isn't a new idea; it came up a lot in the early days of Numeric. PEP 225 (http://legacy.python.org/dev/peps/pep-0225/) has a side discussion on "Impact on named operators" that starts off with: The discussions made it generally clear that infix operators is a scarce resource in Python, not only in numerical computation, but in other fields as well. Several proposals and ideas were put forward that would allow infix operators be introduced in ways similar to named functions. We show here that the current extension does not negatively impact on future extensions in this regard. The future extension was never written as a PEP because 225 and its competitors were were all deferred/abandoned. Also, most of the anticipated use cases for it back then were solved in other ways. The question is whether there are _other_ use cases that make the idea worth reviving. The preferred syntax at that time was @opname. There are other alternatives in that PEP, but they all look a lot worse. Nobody proposed the `opname` because it meant repr(opname), but in 3.x that isn't a problem, so I'm going to use that instead, because… In Haskell, you can turn prefix function into an infix operator by enclosing it in backticks, and turn any infix operator into a prefix function by enclosing it in parens. (Ignore the second half of that, because Python has a small, fixed set of operators, and they all have short, readable names in the operator module.) And, both in the exception-expression discussion and the while-clause discussion, I noticed that this feature is essential to the way Haskell deals with both of these features without requiring lambdas all over the place. The Numeric community wanted this as a way of defining new mathematical operators. For example, there are three different ways you can "multiply" two vectors—element-wise, dot-product, or cross-product—and you can't spell all of them as a * b. So, how do you spell the others? There were proposals to add a new @ operator, or to double the set of operators by adding a ~-prefixed version of each, or to allow custom operators made up of any string of symbols (which Haskell allows), but none of those are remotely plausible extensions to Python. (There's a reason those PEPs were all deferred/abandoned.) However, you could solve the problem easily with infix functions: m `cross` n m `dot` n In Haskell, it's used for all kinds of things beyond that, from type constructors: a `Pair` b a `Tree` (b `Tree` c `Tree` d) `Tree` e … to higher-order functions. The motivating example here is that exception handling is done with the catch function, and instead of this: catch func (\e -> 0) … you can write: func `catch` \e -> 0 Or, in Python terms, instead of this: catch(func, lambda e: 0) … it's: func `catch` lambda e: 0 … which isn't miles away from: func() except Exception as e: 0 … and that's (part of) why Haskell doesn't have or need custom exception expression syntax. PEP 225 assumed that infix functions would be defined in terms of special methods. The PEP implicitly assumed they were going to convince Guido to rename m.__add__(n) to m."+"(n), so m @cross n would obviously be m."@cross"(n). But failing that, there are other obvious possibilities, like m.__@cross__(n), m.__cross__(n), m.__infix__('cross')(n), etc. But really, there's no reason for special methods at all—especially with PEP 443 generic functions. Instead, it could just mean this: cross(m, n) … just as in Haskell. In fact, in cases where infix functions map to methods, there's really no reason not to just _write_ them as methods. That's how NumPy solves the vector-multiplication problem; the dot product of m and n is just: m.dot(n) (The other two meanings are disambiguated in a different way—m*n means cross-product for perpendicular vectors, element-wise multiplication for parallel vectors.) But this can still get ugly for long expressions. Compare: a `cross` b + c `cross` (d `dot` e) a.cross(b).add(c.cross(d.dot(e))) add(cross(a, b), cross(c, dot(d, e)) The difference between the first and second isn't as stark as between the second and third, but it's still pretty clear. And consider the catch example again: func.catch(lambda e: 0) Unless catch is a method on all callables, this makes no sense, which means method syntax isn't exactly extensible. There are obviously lots of questions raised. The biggest one is: are there actually real-life use cases (especially given that NumPy has for the most part satisfactorily solved this problem for most numeric Python users)? Beyond that: What can go inside the backticks? In Haskell, it's an identifier, but the Haskell wiki (http://www.haskell.org/haskellwiki/Infix_expressions) notes that "In ABC the stuff between backquotes is not limited to an identifier, but any expression may occur here" (presumably that's not Python's ancestor ABC, which I'm pretty sure used backticks for repr, but some other language with the same name) and goes on to show how you can build that in Haskell if you really want to… but I think that's an even worse idea for Python than for Haskell. Maybe attribute references would be OK, but anything beyond that, even slicing (to get functions out of a table) looks terrible: a `self.foo` b a `funcs['foo']` b Python 2.x's repr backticks allowed spaces inside the ticks. For operator syntax this would look terrible… but it does make parsing easier, and there's no reason to actually _ban_ it, just strongly discourage it. What should the precedence and associativity be? In Haskell, it's customizable—which is impossible in Python, where functions are defined at runtime but calls are parsed at call time—but defaults to left-associative and highest-precedence. In Python, I think it would be more readable as coming between comparisons and bitwise ops. In grammar terms: infix_expr ::= or_expr | or_expr "`" identifier "`" infix_expr comparison ::= infix_expr ( comparison_operator infix_expr ) * That grammar could easily be directly evaluated into a Call node in the AST, or it could have a different node (mainly because I'm curious whether MacroPy could do something interesting with it…), like: InfixCall(left=Num(n=1), op=Name(id='foo', ctx=Load()), right=Num(n=2)) Either way, it ultimately just compiles to the normal function-call bytecode, so there's no need for any change to the interpreter. (That does mean that "1 `abs` 2" would raise a normal "TypeError: abs expected at most 1 arguments, got 2" instead of a more specific "TypeError: abs cannot be used as an infix operator", but I don't think that's a problem.) This theoretically could be expanded from operators to augmented assignment… but it shouldn't be; anyone who wants to write this needs to be kicked in the head: a `func`= b
On Fri, Feb 21, 2014 at 2:05 PM, Andrew Barnert
While we're discussing crazy ideas inspired by a combination of a long-abandoned PEP and Haskell idioms (see the implicit lambda thread), here's another: arbitrary infix operators:
a `foo` b == foo(a, b)
Prior discussion: https://mail.python.org/pipermail/python-ideas/2007-January/000050.html Which resulted in a new item in PEP 3099 ("Things that will Not Change in Python 3000"; http://legacy.python.org/dev/peps/pep-3099/ ): * No more backticks. Backticks (`) will no longer be used as shorthand for repr -- but that doesn't mean they are available for other uses. Even ignoring the backwards compatibility confusion, the character itself causes too many problems (in some fonts, on some keyboards, when typesetting a book, etc). I think people using suboptimal fonts and keyboard layouts should find better ones... Cheers, Chris
From: Chris Rebert
On Fri, Feb 21, 2014 at 2:05 PM, Andrew Barnert
wrote: While we're discussing crazy ideas inspired by a combination of a long-abandoned PEP and Haskell idioms (see the implicit lambda thread), here's another: arbitrary infix operators:
a `foo` b == foo(a, b)
Prior discussion: https://mail.python.org/pipermail/python-ideas/2007-January/000050.html
Which resulted in a new item in PEP 3099 ("Things that will Not Change in Python 3000"; http://legacy.python.org/dev/peps/pep-3099/ ): * No more backticks. Backticks (`) will no longer be used as shorthand for repr -- but that doesn't mean they are available for other uses. Even ignoring the backwards compatibility confusion, the character itself causes too many problems (in some fonts, on some keyboards, when typesetting a book, etc).
I think people using suboptimal fonts and keyboard layouts should find better ones...
Thanks for pointing that out. OK, some other syntax then, there's no reason it has to be identical to Haskell. We can even go back to the original PEP's version: a @foo b = foo(a, b) … or, more likely, come up with something better. The important question here is whether there's something _worthy_ of new syntax; what that new syntax should be is just bikeshedding. The other main negative consideration from that thread came from mapping custom operators to custom magic methods. I explained in the initial post why I think that's a bad idea, and not necessary. Just map it to a plain old function call, and use generic dispatch if you need to. I think the mathematical example I gave shows how this can be more readable than normal function or method calls: a @cross b + c @cross (d @dot e) a.cross(b).add(c.cross(d.dot(e))) add(cross(a, b), cross(c, dot(d, e)) And the Haskell `catch` example shows how it _might_ be useful for other things, like higher-order function calls, but I haven't come up with a higher-order _Python_ function where it makes sense—and if there isn't one, the proposal is unnecessary.
On Friday, 21 February 2014, Andrew Barnert
From: Chris Rebert
javascript:;> Sent: Friday, February 21, 2014 2:47 PM
On Fri, Feb 21, 2014 at 2:05 PM, Andrew Barnert
javascript:; wrote:
While we're discussing crazy ideas inspired by a combination of a long-abandoned PEP and Haskell idioms (see the implicit lambda thread), here's another: arbitrary infix operators:
a `foo` b == foo(a, b)
Prior discussion: https://mail.python.org/pipermail/python-ideas/2007-January/000050.html
Which resulted in a new item in PEP 3099 ("Things that will Not Change in Python 3000"; http://legacy.python.org/dev/peps/pep-3099/ ): * No more backticks. Backticks (`) will no longer be used as shorthand for repr -- but that doesn't mean they are available for other uses. Even ignoring the backwards compatibility confusion, the character itself causes too many problems (in some fonts, on some keyboards, when typesetting a book, etc).
I think people using suboptimal fonts and keyboard layouts should find better ones...
Thanks for pointing that out.
OK, some other syntax then, there's no reason it has to be identical to Haskell. We can even go back to the original PEP's version:
a @foo b = foo(a, b)
Please, no! Everything on this list for the past few days seems to have been more and more compact syntax for things that may or may not have a use. I'm in favour of adding new things to the language. I really like the except expressions pep. I look at it and I think, "I might have imagined you could already do that, and it will make a lot of code more readable." I think that is a great test for whether something should be added! But function any class decorators seem to have created a wave of suggestions for non-intuitive syntax based around funny characters and notations. It is for the BDFL to say whether these are Pythonic, but I hope none of them turn out to be. I'm comfortable with a language that is very slightly more verbose than it could be. Python at version 3.4 is very readable. Please let's keep it that way! I'm not getting at anyone - or any particular proposal - but there have been a spate of similar things! N.
On 23/02/2014 23:14, Nicholas Cole wrote:
On Friday, 21 February 2014, Andrew Barnert
mailto:abarnert@yahoo.com> wrote: From: Chris Rebert
javascript:;> Sent: Friday, February 21, 2014 2:47 PM
> On Fri, Feb 21, 2014 at 2:05 PM, Andrew Barnert
javascript:;> > wrote: >> While we're discussing crazy ideas inspired by a combination of a > long-abandoned PEP and Haskell idioms (see the implicit lambda thread), > here's another: arbitrary infix operators: >> >> a `foo` b == foo(a, b) > > Prior discussion: > https://mail.python.org/pipermail/python-ideas/2007-January/000050.html > > Which resulted in a new item in PEP 3099 ("Things that will Not Change > in Python 3000"; http://legacy.python.org/dev/peps/pep-3099/ ): > * No more backticks. > Backticks (`) will no longer be used as shorthand for repr -- but > that doesn't mean they are available for other uses. Even ignoring the > backwards compatibility confusion, the character itself causes too > many problems (in some fonts, on some keyboards, when typesetting a > book, etc). > > > I think people using suboptimal fonts and keyboard layouts should find > better ones... Thanks for pointing that out.
OK, some other syntax then, there's no reason it has to be identical to Haskell. We can even go back to the original PEP's version:
a @foo b = foo(a, b)
Please, no!
Everything on this list for the past few days seems to have been more and more compact syntax for things that may or may not have a use.
I'm in favour of adding new things to the language. I really like the except expressions pep. I look at it and I think, "I might have imagined you could already do that, and it will make a lot of code more readable." I think that is a great test for whether something should be added!
But function any class decorators seem to have created a wave of suggestions for non-intuitive syntax based around funny characters and notations. It is for the BDFL to say whether these are Pythonic, but I hope none of them turn out to be.
I'm comfortable with a language that is very slightly more verbose than it could be. Python at version 3.4 is very readable. Please let's keep it that way!
I'm not getting at anyone - or any particular proposal - but there have been a spate of similar things!
N.
Massive +1 from me as you match my sentiments entirely. -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
On 24 February 2014 09:14, Nicholas Cole
I'm comfortable with a language that is very slightly more verbose than it could be. Python at version 3.4 is very readable. Please let's keep it that way!
I'm not getting at anyone - or any particular proposal - but there have been a spate of similar things!
Don't worry, this is just an artefact of the brainstorming nature of python-ideas. Most of the ideas that are posted here won't be a good fit for Python, and *that's OK*. This applies even to the ideas that are posted by experienced core developers - those of us that have been around for long enough to build a pretty good intuition for what counts as "Pythonic" may skip the python-ideas phase of the process when we're confident an idea is a good one and go straight to python-dev or the issue tracker. The end result is that python-ideas discussions related to more esoteric proposals will typically churn around for a while, and then dwindle away naturally. Sometimes more experienced list participants will attempt to nudge such threads in more productive directions, but other times we'll just let them take their natural course (which of those happens will depend mainly on whether there seems to be a possible seed of a good idea in the initial proposal, and how much time people have available to participate in the discussion). Occasionally, someone will hit on something that actually seems promising, and a PEP or a new third party PyPI module is born. PEP 463's except expressions are the most recent example of that, and PEP 450's statistics module is an example of one that went through the process of starting life as a third party module on PyPI (statslib). Other times, a PEP will be born that isn't actually ready for submission to python-dev, but instead acts as a historical record for a problem that is interesting but difficult to solve in a Pythonic way (my own PEPs 403 and 3150 fall into that category). The other thing to remember is that many of the more conservative members of the core development team *don't* participate in python-ideas (making that possible is one of the main reasons they're two separate lists), so even if a proposal gets to the point of being submitted as a PEP, that's still no guarantee that the PEP will be *accepted*. python-dev review may highlight issues that we missed here on python-ideas, and while those can often be fixed by slight tweaks to the PEP, sometimes they may be judged to represent such a fatal flaw that the PEP will be explicitly rejected rather than being revised. So yeah, it's part of the nature of python-ideas to be an environment for relatively free-wheeling "throw ideas at the wall to see what sticks" discussions, as well as history lessons on why things are the way they are, and providing advice on how to build a persuasive case for a particular change. By contrast, python-dev is more focused on the filtering stage of answering the question "Does this change represent an overall improvement to Python?", and the default answer to that question is always going to be No (simply on the grounds that the number of ways we could make Python worse is unbounded, so any new proposal needs to make a persuasive case for how that *particular* change actually makes Python better). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Feb 23, 2014, at 15:14, Nicholas Cole
a @foo b = foo(a, b)
Please, no!
Everything on this list for the past few days seems to have been more and more compact syntax for things that may or may not have a use.
Please, no!
Everything on this list for the past few days seems to have been more and more compact syntax for things that may or may not have a use.
This thread was a spinoff from those other threads, an attempt to see whether a single change could make many of those other proposals unnecessary. I'm not sure it is, or even could be, successful at that. Unfortunately, nobody is even looking at the intended use cases, instead just assuming that this must only be useful for mathematical/numerical code (which, as I've said from the start, I think has been mostly a solved problem since numpy), and therefore either jumping off into how they'd like that to work, or knee-jerk responding that we don't need it. Obviously I've done something wrong in presenting the idea. I wish I knew what it was.
I like infix functions, but am no fan of the backtick syntax. Of all the
things that backticks mean to people: quotes, finger-quotes, code-snippets,
interpolate, whatever, "make me infix" is not one of them. Hard to type on
a keyboard is almost a non-reason in comparison. I know Haskell does it,
but... not that many people have used Haskell.
Another idea would be to go the Scala path and turn *methods* into infix
operators, by letting you call *point_a.add(point_b)* as *point_a add
point_b* or *point_a @add point_b *or some other special sigil. This is
semantically different from using infix functions because your infix
operators now do not clutter up your namespace, since they would be
properties of the LHS object. Not sure if it's "Pythonic", whatever that
means, but it's a neat idea that has found a lot of success in the Scala
world and I think is superior to the various other mechanisms for
infix-functions/operator-overloading that I see in Python/Ruby/C#/F#/etc.
On Fri, Feb 21, 2014 at 8:57 PM, Stephen J. Turnbull
Chris Rebert writes:
I think people using suboptimal fonts and keyboard layouts should find better ones...
Replacement is not yet an option for people with suboptimal eyes and fingers.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Feb 21, 2014, at 21:18, Haoyi Li
I like infix functions, but am no fan of the backtick syntax. Of all the things that backticks mean to people: quotes, finger-quotes, code-snippets, interpolate, whatever, "make me infix" is not one of them. Hard to type on a keyboard is almost a non-reason in comparison. I know Haskell does it, but... not that many people have used Haskell.
Another idea would be to go the Scala path and turn methods into infix operators, by letting you call point_a.add(point_b) as point_a add point_b or point_a @add point_b or some other special sigil. This is semantically different from using infix functions because your infix operators now do not clutter up your namespace, since they would be properties of the LHS object. Not sure if it's "Pythonic", whatever that means, but it's a neat idea that has found a lot of success in the Scala world and I think is superior to the various other mechanisms for infix-functions/operator-overloading that I see in Python/Ruby/C#/F#/etc.
As I said in the initial message, this is exactly what the Numeric guys suggested in the PEP back in the 2.1 days: "a @foo b" resolves to something like "a.__foo__(b)". They also wanted it to fall back to b.__rfoo__(a)". By "something like" I do not mean "exactly". The original proposal relied on the fact that quoted method names, with non-identifier symbols in them, would replace double underscore names, so you'd have "@foo" and "r@foo" (and today's __add__ would be "+"). Anyway, I think functions make more sense than methods here, but I already explained why and don't want to repeat it.
As I said in the initial message, this is exactly what the Numeric guys suggested in the PEP back in the 2.1 days
Ah I missed that point, sorry for the noise
On Sat, Feb 22, 2014 at 1:04 AM, Andrew Barnert
On Feb 21, 2014, at 21:18, Haoyi Li
wrote: I like infix functions, but am no fan of the backtick syntax. Of all the things that backticks mean to people: quotes, finger-quotes, code-snippets, interpolate, whatever, "make me infix" is not one of them. Hard to type on a keyboard is almost a non-reason in comparison. I know Haskell does it, but... not that many people have used Haskell.
Another idea would be to go the Scala path and turn *methods* into infix operators, by letting you call *point_a.add(point_b)* as *point_a add point_b* or *point_a @add point_b *or some other special sigil. This is semantically different from using infix functions because your infix operators now do not clutter up your namespace, since they would be properties of the LHS object. Not sure if it's "Pythonic", whatever that means, but it's a neat idea that has found a lot of success in the Scala world and I think is superior to the various other mechanisms for infix-functions/operator-overloading that I see in Python/Ruby/C#/F#/etc.
As I said in the initial message, this is exactly what the Numeric guys suggested in the PEP back in the 2.1 days: "a @foo b" resolves to something like "a.__foo__(b)". They also wanted it to fall back to b.__rfoo__(a)".
By "something like" I do not mean "exactly". The original proposal relied on the fact that quoted method names, with non-identifier symbols in them, would replace double underscore names, so you'd have "@foo" and "r@foo" (and today's __add__ would be "+").
Anyway, I think functions make more sense than methods here, but I already explained why and don't want to repeat it.
Andrew Barnert wrote:
The Numeric community wanted this as a way of defining new mathematical operators. ... you could solve the problem easily with infix functions:
m `cross` n m `dot` n
For me that totally misses one of the main points of infix operators, which is that they're *concise*. There's little advantage in writing the above rather than cross(m, n) dot(m, n) -- Greg
For me that totally misses one of the main points of infix operators, which is that they're *concise*. There's little advantage in writing the above rather than
On the other hand, it does give you the other reason for infix operators,
which is that the evaluation order of the things happening flow from left
to right, rather than right-to-left or around in a spiral.
On Fri, Feb 21, 2014 at 5:16 PM, Greg Ewing
Andrew Barnert wrote:
The Numeric community wanted this as a way of defining new mathematical operators. ... you could solve the problem easily with infix functions:
m `cross` n m `dot` n
For me that totally misses one of the main points of infix operators, which is that they're *concise*. There's little advantage in writing the above rather than
cross(m, n) dot(m, n)
-- Greg
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Apologies for the mess I'm likely to make trying to reply to a top-post… From Greg Ewing:
For me that totally misses one of the main points of infix operators, which is that they're *concise*. There's little advantage in writing the above rather than
No, it's not about conciseness, it's about readability. I included longer examples in my original post, and repeated them again because people apparently missed them, but here's one of the examples yet again: a `cross` b + c `cross` (d `dot` e) vs. add(cross(a, b), cross(c, dot(d, e)) Sure, it's only one character shorter (or four characters using @cross instead of `cross`), but you can't tell me it's not more readable. The function-calling version is out of order, and has an extra level of nesting, and has a lot more syntactic noise. And look at my motivating example from Haskell: the infix `catch` is not any more _concise_ than the prefix version, but it puts things in the right order (the same order as the proposed exception expression for Python)—and that's why Haskell doesn't need an exception expression. (Well, that plus the ability to create functions from most expressions without lambda, but that's a separate issue.) Again, I'm not sure there are many such cases in Python (after all, 2-argument higher-order functions make up half of Haskell's library, while Python only has a handful of them…), but if there are, they'd benefit the same way. From Haoyi Li:
On the other hand, it does give you the other reason for infix operators, which is that the evaluation order of the things happening flow from left to right, rather than right-to-left or around in a spiral.
Exactly.
Andrew Barnert wrote:
a `cross` b + c `cross` (d `dot` e)
vs.
add(cross(a, b), cross(c, dot(d, e))
Sure, it's only one character shorter (or four characters using @cross instead of `cross`), but you can't tell me it's not more readable.
It's *slightly* better, *maybe*, but it still looks like a mess to my eyes, compared to a % b + c % (d * e) There are several reasons for this version being more readable: * It's far more compact * It's not cluttered up with backticks * It leverages my existing intuition about the relative precedences of the operators involved Precedence is a big problem. You're assuming that backtick operators would have precedence higher than '+'. What if I want to define my own addition-like operator? One precedence isn't going to fit all. -- Greg
From: Greg Ewing
Andrew Barnert wrote:
a `cross` b + c `cross` (d `dot` e)
vs.
add(cross(a, b), cross(c, dot(d, e))
Sure, it's only one character shorter (or four characters using @cross instead of `cross`), but you can't tell me it's not more readable.
It's *slightly* better, *maybe*, but it still looks like a mess to my eyes, compared to
a % b + c % (d * e)
There are several reasons for this version being more readable:
* It's far more compact * It's not cluttered up with backticks * It leverages my existing intuition about the relative precedences of the operators involved
Sure, having an infinite number of easily-distinguishable operators that happen to have the precedence that fits your use case would be create. But we have a very limited set of operators. If you want element-wise multiplication, div, trued, and mod, there aren't any operators left with the right precedence for cross-multiplication, dot-product, and back-division. And even if that weren't an issue, using % to mean mod sometimes and cross-product other times is bound to be confusing to readers. So, the perfect option doesn't exist. Backtick operators always let you write things in order, sometimes but not always let you avoid parentheses, reduce visual noise but don't dispel it completely, and do little or nothing for conciseness. They're not perfect, but they are an improvement.
Precedence is a big problem. You're assuming that backtick operators would have precedence higher than '+'. What if I want to define my own addition-like operator? One precedence isn't going to fit all.
This is another thing I already discussed this in my initial email, and I don't want to repeat myself again. They should all have the same precedence, precisely _because_ they all have the same "visual weight", and no in-built intuitions. Try writing up some examples mixing it with different operators, and I think that, no matter what the name is, they all look like they should bind the same way: 1 * 2 `plus` 3 * 4 == plus(1*2, 3*4), not 1 * plus(2, 3) * 4 1 + 2 `times` 3 + 4 == times(1+2, 3+4), not 1 + times(2, 3) + 4 … or … 1 < 2 `plus` 3 == 1 < plus(2, 3), not plus(1<2, 3) 1 < 2 `times` 3 == 1 < times(2, 3) You can argue about what exactly the precedence should be, but whatever it is, I'm convinced it should be fixed. (In Haskell, there's a default, but it's customizable. And people do create little one-letter functions and infix them with custom precedence and convince themselves that `p` looks like it belongs at a different level than `g`, but I think that's silly in Haskell, and would be even more so in Python.)
On 02/22/2014 03:45 AM, Andrew Barnert wrote:
From: Greg Ewing
Sent: Friday, February 21, 2014 6:12 PM
Andrew Barnert wrote:
a `cross` b + c `cross` (d `dot` e)
vs.
add(cross(a, b), cross(c, dot(d, e))
Sure, it's only one character shorter (or four characters using @cross instead of `cross`), but you can't tell me it's not more readable.
It's *slightly* better, *maybe*, but it still looks like a mess to my eyes, compared to
a % b + c % (d * e)
There are several reasons for this version being more readable:
* It's far more compact * It's not cluttered up with backticks * It leverages my existing intuition about the relative precedences of the operators involved Sure, having an infinite number of easily-distinguishable operators that happen to have the precedence that fits your use case would be create. But we have a very limited set of operators. If you want element-wise multiplication, div, trued, and mod, there aren't any operators left with the right precedence for cross-multiplication, dot-product, and back-division. And even if that weren't an issue, using % to mean mod sometimes and cross-product other times is bound to be confusing to readers. Why not use multiple "operator characters" for user-defined infix operators, like postgreSQL does ?
a *% b ++ c *% (d *. e) We could even define that the characters in the combined operator define precendece so the above could be written as a *% b ++ c *% d *. e where *% has higher precedence than *. because of (* is higher than %) and *. has higher precedence than *% because (. is gher than %) Cheers Hannu
So, the perfect option doesn't exist.
Backtick operators always let you write things in order, sometimes but not always let you avoid parentheses, reduce visual noise but don't dispel it completely, and do little or nothing for conciseness. They're not perfect, but they are an improvement.
Precedence is a big problem. You're assuming that backtick operators would have precedence higher than '+'. What if I want to define my own addition-like operator? One precedence isn't going to fit all.
This is another thing I already discussed this in my initial email, and I don't want to repeat myself again.
They should all have the same precedence, precisely _because_ they all have the same "visual weight", and no in-built intuitions. Try writing up some examples mixing it with different operators, and I think that, no matter what the name is, they all look like they should bind the same way:
1 * 2 `plus` 3 * 4 == plus(1*2, 3*4), not 1 * plus(2, 3) * 4 1 + 2 `times` 3 + 4 == times(1+2, 3+4), not 1 + times(2, 3) + 4
… or …
1 < 2 `plus` 3 == 1 < plus(2, 3), not plus(1<2, 3)
1 < 2 `times` 3 == 1 < times(2, 3)
You can argue about what exactly the precedence should be, but whatever it is, I'm convinced it should be fixed.
(In Haskell, there's a default, but it's customizable. And people do create little one-letter functions and infix them with custom precedence and convince themselves that `p` looks like it belongs at a different level than `g`, but I think that's silly in Haskell, and would be even more so in Python.) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
From: Hannu Krosing
On 02/22/2014 03:45 AM, Andrew Barnert wrote:
Sure, having an infinite number of easily-distinguishable operators that
happen to have the precedence that fits your use case would be create. But we have a very limited set of operators. If you want element-wise multiplication, div, trued, and mod, there aren't any operators left with the right precedence for cross-multiplication, dot-product, and back-division. And even if that weren't an issue, using % to mean mod sometimes and cross-product other times is bound to be confusing to readers.
Why not use multiple "operator characters" for user-defined infix operators, like postgreSQL does ?
a *% b ++ c *% (d *. e)
First, I'm not sure why everyone is focusing on the mathematical examples. As I said front he start, I think NumPy and friends have mostly solved the problem of doing math readably in Python, and the only reason custom operators would likely be useful would be if we had _other_ use cases, probably (but not necessarily) akin to the higher-order functions people frequently inline in Haskell. Of course in Haskell, many of those _are_ written as symbols, but I doubt anyone wants that for Python. I could see "spam `compose` eggs" in Python, but not "spam . eggs". Anyway, sticking with the mathematical cases, Haskell does what you're proposing. And it was discussed for Python Numeric in the 2.1 days. I don't think PEP 225 covers why they rejected the idea in favor of just doubling the number of operators by adding a special ~ prefix, but there are two major problems. First, none of those sequences has any inherent meaning. I can guess what @cross means. If you just double the set of operators by allowing a ~ prefix, I can guess that ~* is a variant on multiplication (I do have to know whether you're using ~* for element-wise and * for object-wise, Matlab-style, or vice-versa, R-style, but that's just one piece of information per project). If you have a whole suite of free-form operators made up of symbol strings, I have absolutely no idea what *% means. Maybe you know what you intended there, but would you know after three weeks of working on a different project? This is a serious problem with Haskell; I can't read someone's code unless I have his project's operator-defining stub open so I know whether he used <. for sorting, compose-composing, backward folding, or something I never even heard of that I have to work through. Second, many such sequences are ambiguous. What does 3*.2 mean? Is it your custom *. operator, or a normal * multiplying 3 and .2? It's not just the dot; + and - are unary prefix operators, = is a different kind of syntax, etc. You could use whitespace to distinguish, so 2 %+ 3 is op%+(2, 3) but 2%+3 is mod(2, +3), but that's a pretty big change to the way the parser works. Also, there's a reason the "spaces around binary operators" rule is in the PEP 8 style guide rather than the language: usually it makes your code more readable to follow the guideline, but sometimes it makes it less readable.
We could even define that the characters in the combined operator define
precendece so the above could be written as
a *% b ++ c *% d *. e
where *% has higher precedence than *. because of (* is higher than %) and *. has higher precedence than *% because (. is gher than %)
Well, * and % are actually the same precedence in Python, and . isn't an operator so it doesn't have a precedence, but I get your point. This has been discussed for Haskell a few times; see the thread starting http://www.haskell.org/pipermail/haskell-cafe/2006-October/018884.html for one example. I don't know that anyone ever thought it through for Python. It might work. It would certainly encourage people to use operators starting with * only to use multiplication-like things instead of whatever-they-want, since it's going to get multiplication-like precedence and associativity. It might be worth working through some detailed examples.
From: Hannu Krosing
On 02/22/2014 03:45 AM, Andrew Barnert wrote:
Sure, having an infinite number of easily-distinguishable operators that
happen to have the precedence that fits your use case would be create. But we have a very limited set of operators. If you want element-wise multiplication, div, trued, and mod, there aren't any operators left with the right precedence for cross-multiplication, dot-product, and back-division. And even if that weren't an issue, using % to mean mod sometimes and cross-product other times is bound to be confusing to readers.
Why not use multiple "operator characters" for user-defined infix operators, like postgreSQL does ?
a *% b ++ c *% (d *. e)
First, I'm not sure why everyone is focusing on the mathematical examples. As I said front he start, I think NumPy and friends have mostly solved the problem of doing math readably in Python, and the only reason custom operators would likely be useful would be if we had _other_ use cases, probably (but not necessarily) akin to the higher-order functions people frequently inline in Haskell. Of course in Haskell, many of those _are_ written as symbols, but I doubt anyone wants that for Python. I could see "spam `compose` eggs" in Python, but not "spam . eggs". Anyway, sticking with the mathematical cases, Haskell does what you're proposing. And it was discussed for Python Numeric in the 2.1 days. I don't think PEP 225 covers why they rejected the idea in favor of just doubling the number of operators by adding a special ~ prefix, but there are two major problems. First, none of those sequences has any inherent meaning. I can guess what @cross means. If you just double the set of operators by allowing a ~ prefix, I can guess that ~* is a variant on multiplication (I do have to know whether you're using ~* for element-wise and * for object-wise, Matlab-style, or vice-versa, R-style, but that's just one piece of information per project). If you have a whole suite of free-form operators made up of symbol strings, I have absolutely no idea what *% means. Maybe you know what you intended there, but would you know after three weeks of working on a different project? This is a serious problem with Haskell; I can't read someone's code unless I have his project's operator-defining stub open so I know whether he used <. for sorting, compose-composing, backward folding, or something I never even heard of that I have to work through. Second, many such sequences are ambiguous. What does 3*.2 mean? Is it your custom *. operator, or a normal * multiplying 3 and .2? It's not just the dot; + and - are unary prefix operators, = is a different kind of syntax, etc. You could use whitespace to distinguish, so 2 %+ 3 is op%+(2, 3) but 2%+3 is mod(2, +3), but that's a pretty big change to the way the parser works. Also, there's a reason the "spaces around binary operators" rule is in the PEP 8 style guide rather than the language: usually it makes your code more readable to follow the guideline, but sometimes it makes it less readable.
We could even define that the characters in the combined operator define
precendece so the above could be written as
a *% b ++ c *% d *. e
where *% has higher precedence than *. because of (* is higher than %) and *. has higher precedence than *% because (. is gher than %)
Well, * and % are actually the same precedence in Python, and . isn't an operator so it doesn't have a precedence, but I get your point. This has been discussed for Haskell a few times; see the thread starting http://www.haskell.org/pipermail/haskell-cafe/2006-October/018884.html for one example. I don't know that anyone ever thought it through for Python. It might work. It would certainly encourage people to use operators starting with * only to use multiplication-like things instead of whatever-they-want, since it's going to get multiplication-like precedence and associativity. It might be worth working through some detailed examples.
On Sat, Feb 22, 2014 at 5:59 PM, Andrew Barnert
From: Hannu Krosing
Sent: Saturday, February 22, 2014 11:34 AM
Sure, having an infinite number of easily-distinguishable operators
On 02/22/2014 03:45 AM, Andrew Barnert wrote: that
happen to have the precedence that fits your use case would be create. But we have a very limited set of operators. If you want element-wise multiplication, div, trued, and mod, there aren't any operators left with the right precedence for cross-multiplication, dot-product, and back-division. And even if that weren't an issue, using % to mean mod sometimes and cross-product other times is bound to be confusing to readers.
Why not use multiple "operator characters" for user-defined infix operators, like postgreSQL does ?
a *% b ++ c *% (d *. e)
First, I'm not sure why everyone is focusing on the mathematical examples.
I have not seen a compelling use case for infix expressions other than mathematical operators which currently require a method, thereby forcing an order that does not match what we are used to writing. As has been argued, conciseness is a virtue for the readability of complex mathematical expressions. Allowing Unicode symbols for operators would be nice in an ideal world, but it seems like in practice it would create a lot of pain for very little gain. So I think the best compromise would be to allow current binary operators to be augmented with a special character, such as ~ or : or `, such that (a) new ambiguities would not be introduced into the grammar, and (b) the precedence and dunder method name would be determined by the current operator. Whether the operator has been "modified" with the special character could be passed as an argument to the dunder method.
I don't think PEP 225 covers why they rejected the idea in favor of just doubling the number of operators by adding a special ~ prefix, but there are two major problems.
First, none of those sequences has any inherent meaning. I can guess what @cross means. If you just double the set of operators by allowing a ~ prefix, I can guess that ~* is a variant on multiplication (I do have to know whether you're using ~* for element-wise and * for object-wise, Matlab-style, or vice-versa, R-style, but that's just one piece of information per project).
Using the special modifier character would tell the user that they have to refer to the library documentation to interpret it. In fact, I think builtins should be prohibited from supporting any of the modified operators, so as to avoid establishing a default interpretation for any of them. But presumably, most of the important use cases (e.g., cross product) would have a conceptually related current operator (*), and would be frequent enough that it would be worth the reader's time to look it up. For rarer cases, there is little harm in just providing a method.
If you have a whole suite of free-form operators made up of symbol strings, I have absolutely no idea what *% means.
Agreed, which is why I would be in favor of restricting ourselves to a single modifier character that is not already an operator. Nathan
From: Nathan Schneider
On Sat, Feb 22, 2014 at 5:59 PM, Andrew Barnert
wrote: From: Hannu Krosing
Sent: Saturday, February 22, 2014 11:34 AM
On 02/22/2014 03:45 AM, Andrew Barnert wrote:
Sure, having an infinite number of easily-distinguishable operators that
happen to have the precedence that fits your use case would be create. But we have a very limited set of operators. If you want element-wise multiplication, div, trued, and mod, there aren't any operators left with the right precedence for cross-multiplication, dot-product, and back-division. And even if that weren't an issue, using % to mean mod sometimes and cross-product other times is bound to be confusing to readers.
Why not use multiple "operator characters" for user-defined infix operators, like postgreSQL does ?
a *% b ++ c *% (d *. e)
First, I'm not sure why everyone is focusing on the mathematical examples.
I have not seen a compelling use case for infix expressions other than mathematical operators which currently require a method, thereby forcing an order that does not match what we are used to writing. As has been argued, conciseness is a virtue for the readability of complex mathematical expressions.
For the mathematical case, are you a heavy user of NumPy, SymPy, or other such libraries who finds them unreadable because of this problem? Because my understanding is that most such people don't think it's a serious problem in their real work, which is why PEP 225 hasn't been taken up again. I believe the big trick is thinking of even vectors as multi-dimensional: Multiply two row-vectors and you get element-wise multiplication; multiply a row-vector and a column-vector and you get cross-product. Beyond the mathematical case, let me try to explain again. Why do people want an except expression, when you could just write it as a function? Because there are two problems with this: catch(lambda: 1/n, ZeroDivisionError, lambda e: nan) First, you have to manually "delay" all the expressions by wrapping them in lambdas. Second, everything reads out of order—the important bit here is 1/n, not the fact that it's catching an error. That's why people want to write this: 1/n except ZeroDivisionError as e: nan But what if we had general solutions to both problems? Using Nick's short lambda syntax (actually just PEP 312 in this case) and backticks for infix operators, even though those may not be the best solutions: :1/n `catch` (ZeroDivisionError, :nan) Perfect? No. Good enough that we wouldn't feel compelled to add new syntax to improve it? Maybe. Obviously, if these two features together only solved one use case, and imperfectly at that, they wouldn't be worth it. But consider the with expression someone recently proposed: data = f.read() with open(path) as f (Apologies for using a silly example; I can't remember what the supporting use cases were.) You can write that as a function, but it looks like this: data = withcontext(lambda f: f.read(), open(path)) But with short lambdas and infix functions, it's a lot better: data = :?.read() `withcontext` open(path) Are there other "missing features" in Python that could be worked around with short lambdas and inline functions, instead of having to add new syntax for each one? (Could we have avoided some of the new features added in the past decade?) This is the part I'm not sure of. It may be that, at least for the kinds of programs people write today, Python has almost all of the constructions almost anyone needs, and we're better off filling in the last one or two gaps than looking for a general solution. As a side note, Clojure uses a different trick in a similar way. For example, list comprehensions are plain old functions, just as in Racket and other Lisp descendants—but its Smalltalk-style infix parameter names make it look a lot more like Python/Haskell/Miranda's special syntax: [p**2 for p in primes while p<1000 if p%2] (for [p primes :when (odd? p) :while (p<1000)] p**2) And the same trick could work for a catch function to make it even nicer, and it could have solved ternary expressions like the conditional: x = :1/n :catch ZeroDivisionError :then :NaN x = :n :then :1/n :orelse :NaN … but I have absolutely no idea how to fit that idea into Python. But back to your take on the mathematical case:
So I think the best compromise would be to allow current binary operators to be augmented with a special character, such as ~ or : or `, such that (a) new ambiguities would not be introduced into the grammar, and (b) the precedence and dunder method name would be determined by the current operator. Whether the operator has been "modified" with the special character could be passed as an argument to the dunder method.
This is nearly identical to PEP 225, except that you're passing a "tilde-prefixed" flag to the existing dunder methods instead of doubling the number of dunder methods. I think in general it's better to have separate functions for separate purposes than to add flag parameters, but I can see the argument that this is a special case: likely all of the operators would treat that flag the same way, so it could just be a matter of putting, say, "if tilde: self = self.lift_and_transpose()" at the start of each method, or doing the equivalent with a decorator on each method, or dynamically applying that decorator to all of the methods, or …
Using the special modifier character would tell the user that they have to refer to the library documentation to interpret it. In fact, I think builtins should be prohibited from supporting any of the modified operators, so as to avoid establishing a default interpretation for any of them.
Yes, PEP 225 has an extensive argument for why this is the best interpretation. (The short version is: Matlab vs. R. But there's more to it; it's worth reading.)
If you have a whole suite of free-form operators made up of symbol strings, I have absolutely no idea what *% means.
Agreed, which is why I would be in favor of restricting ourselves to a single modifier character that is not already an operator.
Unfortunately, ~ _is_ already an operator, and can in fact be combined with the unary + and - operators: >>> ~3 -4 >>> ~-3 2
On Sat, Feb 22, 2014 at 08:34:51PM +0100, Hannu Krosing wrote:
Why not use multiple "operator characters" for user-defined infix operators, like postgreSQL does ?
a *% b ++ c *% (d *. e)
How would that work in Python? Give an example (a toy or pretend example is fine) for how I would define an operator ^& for ints.
We could even define that the characters in the combined operator define precendece so the above could be written as
Who is "we"? The programmer of the module, or the Python core devs? In other words, are the precedences set once, in the language, or in each module that uses them? If I import an object x from one module with precedences set one way, and an object y from another module with precedences set another way, and try to use them in the same expression, whose precedences win? An unrelated thought: there are currently eight ASCII symbols used for non-comparison operators in Python: ~%^&*-+/ (did I miss any?). Of those, three can be used as unary operators ~-+ and so should not be allowed in the second position. So with eight symbols available in the first position, and five in the second, that gives us 40 new infix operators. But one of them ** is already used, so 39. To my mind, 39 arbitrary infix operators is both too many and too few. Too many, because, really, who needs 39 new infix operators? Who will remember them all? But too few, because it means that when you want a new operator, you have to force it into one of those 39 (48 if you can re-use one of existing operators, 52 if you include comparison operators
= < <= but not equality and inequality). For example, I'd like to use ∪ and ∩ for set union and intersection, but instead those two operations get mapped to & and | and I can never remember which is which. I'd like to use operators such as ⊕ ⊗. But so long as Python operators are ASCII only, that cannot happen.
But then, I have a mathematics background. I'm having trouble thinking of examples outside of maths where I would want to define infix operators at all, instead of just using a method or function. -- Steven
On Sat, Feb 22, 2014 at 5:23 PM, Steven D'Aprano
For example, I'd like to use ∪ and ∩ for set union and intersection, but instead those two operations get mapped to & and | and I can never remember which is which. I'd like to use operators such as ⊕ ⊗. But so long as Python operators are ASCII only, that cannot happen.
What if Python didn't restrict you to ASCII operators? Here's specifics of how that could work: (1) An extended operator is a *single* math symbol character, e.g., ∩ or ⊕or ⊲ but not ⊲⊲. The set of allowed characters needs to be defined carefully. It could be any Unicode S-class character with the math property. Extended operators can be combined with an equal sign to do in-place updating, e.g., A ∩= B. (2) Extended operators can be declared like this. class sample: def '∩'(self, rhs): return self.value.intersection(sample.get_set(rhs)) def 'r∩'(self, lhs): """Reversed operator.""" return sample.get_set(lhs).intersection(self.value) def 'i∩'(self, rhs): self.value.intersection_update(sample.get_set(rhs)) @staticmethod def get_set(self): try: value = rhs.value except AttributeError: value = rhs return set(value) I'm not sure about enclosing the operator character in quotes. C++ and C# use an operator keyword, while Ruby doesn't mark it at all. Python, of course, uses __oper__ functions for overloading built-in operators. Writing __∩__ would complicate the tokenizer as it would have to recognize this exact syntax and treat it as an identifier while not allowing math symbol characters in other uses in an identifier. And I probably need to write sample['∩'] not sample.∩. Without the quotes or something along those lines, these look too similar: def U(a, b): pass def ∪(a, b): pass (3) All extended operators have the same precedence, between comparisons and bitwise or. Perhaps expressions mixing different extended operators always require parentheses, i.e., (A ∪ B ∩ C) would be a syntax error. To my mind this is way more readable than something like *%+& which looks like someone cursing. :-) --- Bruce Learn how hackers think: http://j.mp/gruyere-security
On Feb 22, 2014, at 19:17, Bruce Leban
On Sat, Feb 22, 2014 at 5:23 PM, Steven D'Aprano
wrote: For example, I'd like to use ∪ and ∩ for set union and intersection, but instead those two operations get mapped to & and | and I can never remember which is which. I'd like to use operators such as ⊕ ⊗. But so long as Python operators are ASCII only, that cannot happen.
What if Python didn't restrict you to ASCII operators?
That was something the PEP 225 (might have got it wrong from memory; I'm on my phone) envisioned as a future solution, in the far off day that Python and source editors could handle Unicode. And we have pretty much reached that day. So I'm not sure why I didn't pick up on this; thanks for bringing it up! However, there is still a big problem typing these symbols. Unicode hasn't meant a resurgence in APL, after all... A handful of them are available as opt-sequences on Mac, but most aren't. And on other platforms, where you have to use alt+keypad, none of them are typeable. Of course powerful enough editors can easily take care of this problem, but is it acceptable to say "if you want to use fancy operators, you have to use emacs, vi, or some not-yet-released version of Eclipse or PyCharm"? (Maybe it is; this is a real question, not rhetorical...) And it's not just editing code; if I want to import a module at the interactive prompt in my terminal or IDLE and call one of these functions, if I want to write a code snippet in an email or a StackOverflow answer, etc., I need an input method that works there (or I need to copy and paste from emacs). Finally, this is even _more_ specific to mathematical use cases. If I want to inline a higher-order function, there will likely be no obvious mathematical symbol for most of them.
Here's specifics of how that could work:
(1) An extended operator is a single math symbol character, e.g., ∩ or ⊕ or ⊲ but not ⊲⊲. The set of allowed characters needs to be defined carefully. It could be any Unicode S-class character with the math property. Extended operators can be combined with an equal sign to do in-place updating, e.g., A ∩= B.
That seems like a good first cut at the rule.
(2) Extended operators can be declared like this.
class sample: def '∩'(self, rhs): return self.value.intersection(sample.get_set(rhs))
That's the same syntax the PEP envisioned. They also wanted to change __add__ to '+', which probably seemed more reasonable in the early 2.x days than after 20 years of history.
And I probably need to write sample['∩'] not sample.∩.
This part I don't like. In Python, attribute access and keyed access are different; namespaces are not dictionaries and vice-versa. And it argues against the quoted operator syntax (unless you want to allow quotes for attribute access...). But I'm not sure what the right answer is.
(3) All extended operators have the same precedence, between comparisons and bitwise or.
I definitely agree with a fixed precedence (and associativity) that they all share. That specific choice is the same precedence I suggested for `operators`. But I did that based on writing a bunch of expressions mixing operators and seeing what looked most "natural". I'd have to look at the same examples with Unicode operators to see if it still looks right.
Perhaps expressions mixing different extended operators always require parentheses, i.e., (A ∪ B ∩ C) would be a syntax error.
With backtick operators this doesn't seem necessary, but for Unicode symbols, I think you're right. The natural way to read that is for intersection to bind more tightly, which is not what the rule would do. So a SyntaxError is better than misleading code.
To my mind this is way more readable than something like *%+& which looks like someone cursing. :-)
On Sat, Feb 22, 2014 at 9:30 PM, Andrew Barnert
On Feb 22, 2014, at 19:17, Bruce Leban
wrote: However, there is still a big problem typing these symbols. Unicode hasn't meant a resurgence in APL, after all... A handful of them are available as opt-sequences on Mac, but most aren't. And on other platforms, where you have to use alt+keypad, none of them are typeable. Of course powerful enough editors can easily take care of this problem, but is it acceptable to say "if you want to use fancy operators, you have to use emacs, vi, or some not-yet-released version of Eclipse or PyCharm"? (Maybe it is; this is a real question, not rhetorical...) And it's not just editing code; if I want to import a module at the interactive prompt in my terminal or IDLE and call one of these functions, if I want to write a code snippet in an email or a StackOverflow answer, etc., I need an input method that works there (or I need to copy and paste from emacs).
These are legitimate questions. I would hate to see substitute n-graphs like in C: https://en.wikipedia.org/wiki/Digraphs_and_trigraphs. And having this feature and then having everyone write A <<intersect>> B instead doesn't have much of an advantage. On the other hand, I haven't seen any replies from people saying they can't read these characters. So the real issue might be the inconvenience of typing. I wonder if the people who would use them are going to be typing them anyway in other contexts. Finally, this is even _more_ specific to mathematical use cases. If I want
to inline a higher-order function, there will likely be no obvious mathematical symbol for most of them.
I'm not so sure about that. Check out this list of 949 Unicode math symbols. http://www.fileformat.info/info/unicode/category/Sm/list.htm And depending on the definition, there could be even more than that. While some of these have strong meanings (like variations on standard mathematical operators, integral signs, etc.) a lot seem more adaptable.
And I probably need to write sample['∩'] not sample.∩.
This part I don't like. In Python, attribute access and keyed access are different; namespaces are not dictionaries and vice-versa.
And it argues against the quoted operator syntax (unless you want to allow quotes for attribute access...).
Oops. Of course I meant getattr(sample, '∩'). I've been switch back and forth a lot between Python and Javascript lately and it's easy to get that confused. Yes, that's not as attractive as sample.__add__ but do we write this that often? --- Bruce Learn how hackers think: http://j.mp/gruyere-security
On 23 February 2014 07:17, Bruce Leban
On the other hand, I haven't seen any replies from people saying they can't read these characters. So the real issue might be the inconvenience of typing. I wonder if the people who would use them are going to be typing them anyway in other contexts.
Some things that immediately strike me: 1. The Windows console is not Unicode-friendly and displaying non-ASCII source code is still problematic. 2. Encoding detection is *not* a solved problem, and it's certainly not universally implemented in editors. It may look like it on Unix systems where the system encoding is generally UTF8 these days, but on Windows the system codepage is typically a 256-character one and working with anything different (which you'd need to for this proposal) means remembering to specify explicit encodings quite frequently. 3. Tools like grep may or may not work with extended characters - they don't on Windows, but I don't know how much of the fault for that lies with the Windows console. From what I've seen of the grep sources they might but they rely on char *argv which implies that the C runtime support for setting up argv might be relevant here (and on Windows that does *not* handle extended characters) The general theme here is obviously "Windows" so maybe that's really what I'm saying. But it does mean that Andrew Barnert's comment "in the far off day that Python and source editors could handle Unicode. And we have pretty much reached that day." may not be true unless you're willing to ignore Windows users. Paul
On Feb 23, 2014, at 3:48, Paul Moore
... Windows
The general theme here is obviously "Windows" so maybe that's really what I'm saying. But it does mean that Andrew Barnert's comment "in the far off day that Python and source editors could handle Unicode. And we have pretty much reached that day." may not be true unless you're willing to ignore Windows users.
You're right, I misspoke imprecisely. We've reached the day where it's _in sight_, but now where it's usable fact. But that still means we can evaluate the idea a lot better now. Even if Windows 9 were UTF-8-centric, or Python 4 used nothing but the UTF-16 interfaces in Windows and were PowerShell-friendly, there would still be all the problems I raised (like entry methods--not just for editors, but for the interactive interpreter, web sites and mail clients where you want to post code samples, etc.), and I think it's an open but maybe answerable question whether those problems are acceptable as a tradeoff for the benefits (as opposed to 20 years ago, where you could really only answer by guessing what a Unicode-friendly platform would look like). Of course this is me talking about Bruce's idea. I don't want to put words in his mouth. And I also don't want people to forget that _my_ idea was using infix functions for _non-mathematical_ cases, and this is only a minor sideline to me.
On Sun, Feb 23, 2014 at 12:47:46PM -0800, Andrew Barnert wrote:
Of course this is me talking about Bruce's idea. I don't want to put words in his mouth. And I also don't want people to forget that _my_ idea was using infix functions for _non-mathematical_ cases, and this is only a minor sideline to me.
I can't think of many useful examples of infix operators that aren't mathematical, and even fewer that aren't just as easily written as methods or functions. I think that's why there are so few non-mathematical examples given in this thread. Apart from, say, taking the union and intersection of two dicts, I can't really think of anything where I'd want this outside of mathematics. In the absense of any compelling use-case for allowing arbitrary non-mathematical infix operators, I'm -1 on adding all this complexity and (to be frank) cryptic ugliness to the code. 99 times out of 100, the right answer is "use a method or function", and the remaining time, using a method or function is nearly always acceptable. All the low-hanging fruit is taken: between + - * / and perhaps a few other operators, nearly all the common and important use-cases are covered. -- Steven
Steven D'Aprano writes:
I can't think of many useful examples of infix operators that aren't mathematical, and even fewer that aren't just as easily written as methods or functions.
The copula, not to mention all non-mathematical transitive verbs, in English? This feature would make it a lot easier to write DSLs in Python. (I don't know if that would necessarily be a good thing.)
On Mon, Feb 24, 2014 at 11:08:47AM +0900, Stephen J. Turnbull wrote:
Steven D'Aprano writes:
I can't think of many useful examples of infix operators that aren't mathematical, and even fewer that aren't just as easily written as methods or functions.
The copula, not to mention all non-mathematical transitive verbs, in English? This feature would make it a lot easier to write DSLs in Python. (I don't know if that would necessarily be a good thing.)
Copula are "linking verbs" like "be", "is", "was" or "becomes". Under what circumstances would you want to write such operators? Using a single backtick ` as the "custom operator" syntax, I came up with: marriage = two `become one while not (self `be all_that_you_can_be): self.improve() but really, these are just jokes. I'm still no closer to actual use-cases. Any infix binary operator a `op b can be written as a function of two arguments, op(a, b). It's not that there are no use-cases for infix binary operators, but that all the obvious ones (such as string concatenation, comparisons, list repetition, etc.) already exist and the rest can nearly always be easily written as functions. -- Steven
On Feb 24, 2014, at 7:28, Steven D'Aprano
but really, these are just jokes. I'm still no closer to actual use-cases.
I will repeat one that I already posted, but try to put it differently: a using function that wraps a with statement. Every kind of statement in Python would sometimes be useful as an expression. You can always wrap the statement in a function, but it looks ugly--even with Nick's concise lambda proposal. For example, lets say we wrapped with, try/except, and if/else in functions named using, catch, and cond: data = using(:?.read(), open(path)) buf = catch(:read_file(path), (FileNotFoundError, :'') b = cond(a, :1/a, :NaN) All of these are possible today, but were not sufficient to curb the desire for with, except, and if expressions. One was already added, one is under consideration, and the last gets suggested at least once/year. But if you could infix those functions: data = :?.read() `using` open(path) buf = :read_file(path) `catch` FileNotFoundError, :'' b = a `cond` :1/a, :NaN Would they then be good enough to obviate the need for new syntax? I assume not having to add a corresponding expression for every statement is a desirable goal. The only question is whether this would help achieve that goal.
Any infix binary operator a `op b can be written as a function of two arguments, op(a, b).
Of course. The whole point of the idea--in the very first paragraph of the initial post--is that a `op` b would be compile to the exact same code as op(a, b). (Others have suggested that maybe a method would be better, but that doesn't change the point.) This is pure syntactic sugar for those functions, nothing else.
It's not that there are no use-cases for infix binary operators, but that all the obvious ones (such as string concatenation, comparisons, list repetition, etc.) already exist and the rest can nearly always be easily written as functions.
The question is not whether they can be written as functions, but whether those functions' calls can be read more easily with infix syntax.
On Mon, Feb 24, 2014 at 04:11:19PM -0800, Andrew Barnert wrote:
On Feb 24, 2014, at 7:28, Steven D'Aprano
wrote: but really, these are just jokes. I'm still no closer to actual use-cases.
I will repeat one that I already posted, but try to put it differently: a using function that wraps a with statement.
Every kind of statement in Python would sometimes be useful as an expression. You can always wrap the statement in a function, but it looks ugly--even with Nick's concise lambda proposal. For example, lets say we wrapped with, try/except, and if/else in functions named using, catch, and cond:
data = using(:?.read(), open(path)) buf = catch(:read_file(path), (FileNotFoundError, :'') b = cond(a, :1/a, :NaN)
All of these are possible today,
You must have a different understanding of "possible" than I do, because they all give me syntax errors.
but were not sufficient to curb the desire for with, except, and if expressions. One was already added, one is under consideration, and the last gets suggested at least once/year.
But if you could infix those functions:
data = :?.read() `using` open(path) buf = :read_file(path) `catch` FileNotFoundError, :'' b = a `cond` :1/a, :NaN
Would they then be good enough to obviate the need for new syntax?
All of those are completely unreadable messes to me. If you're trying to sell the idea of one new controversial feature, you really shouldn't demonstrate it with another controversial new syntax. Let me try to make your argument for you, I hope fairly. You're hoping that by introducing custom infix binary operators, you can replace syntax for (say) result = 23 if condition else 42 with result = 23 `if_operator` condition, 42 or something similar. The fact that ternary if already exists isn't important here -- it is your argument that if we had custom infix operators, we wouldn't have needed to add ternary if. I don't think this suggestion comes even close to working well. Without proper support interpreter support for ternary operators, forcing three arguments around a binary operator is never going to look right, and may have precedence issues. And it lacks support for lazy evaluation, which means you're stuck with an ugly work-around of wrapping operands in functions and delaying calling the function when you want laziness: # define the two getter functions elsewhere: result = (get_true_value `if_operator` condition, get_false_value)() # in-place, using lambda: result = ((lambda: true_expression) `if_operator` condition, (lambda: false_expression))() # using proposed lambda alternative result = (:true_expression `if_operator` condition, :false_expression)() None of which are as good as the syntax we have for ternary if. This suggests that dedicated syntax beats custom syntax. [Aside: one can remove the outer pair of parentheses by embedding the final call inside the custom operator, but that would mean you *always* have to use functions as arguments, even when lazy evaluation is unnecessary. That's a bad idea.] Not to put too fine a point to it, mapping ternary statements like if...else and try...except to an infix binary operator sucks. Fortunately there are only two of those, one is already syntax and the other is subject to a PEP. What's left? There aren't that many expressions left... for-loops can be written as list comps, and besides they require a name-binding so don't fit well to this model; del doesn't need to be an expression, because it doesn't return anything; likewise for pass; assignment-as-expression goes against the grain of the language; import can be written as __import__ if you really must; it might be handy to raise an exception from inside an expression, but I don't see that this maps to your proposal; classes can be created with type(); and functions with lambda. Have I missed anything? This leaves perhaps while loops and with statement, and I don't think that they're going to map any better to this proposal than did if. result = expression `while_operator` condition doesn't work for me. It's unclear that the result is a list (or a tuple? set? lazy generator?), but even if we decided that you can map while and with statements to infix binary operators, I don't think that having an over-generalised system like this is an improvement over dedicated while-comprehensions and with-expressions. Besides, there is strong value in having (say) a single way to spell a for expression, namely [blah for name in seq], rather than a plethora of custom infix operators: # assume there is magic for name-binding somewhere result = blah `for_operator` name, seq result = blah `for_loop` seq, name result = name, blah `do` seq result = seq `repeat` name, blah etc., depending on the personal preferences of the coder writing it. I've given this idea a good shake, and I think it's a limp rag. I don't think that mapping statements to infix binary operators in this way is a workable idea. I'll be honest, it seems to be *so grossly and fundamentally unworkable* to me that I'm kind of astonished that you've raised it not once but twice, instead of discarding it as ridiculous, like the idea of a paper submarine or a hot iron balloon. Perhaps there are other requirements needed to make this work that you haven't mentioned and I haven't thought of? Sorry to be so dismissive, but that's the truth of it. [...]
Any infix binary operator a `op b can be written as a function of two arguments, op(a, b).
Of course. The whole point of the idea--in the very first paragraph of the initial post--is that a `op` b would be compile to the exact same code as op(a, b). (Others have suggested that maybe a method would be better, but that doesn't change the point.) This is pure syntactic sugar for those functions, nothing else.
It's not that there are no use-cases for infix binary operators, but that all the obvious ones (such as string concatenation, comparisons, list repetition, etc.) already exist and the rest can nearly always be easily written as functions.
The question is not whether they can be written as functions, but whether those functions' calls can be read more easily with infix syntax.
That's not the only question. A very important question is whether it is worth adding all this generalized infix operator machinary just so that people can write: x `becomes` y rather than becomes(x, y) If this is *pure syntactic sugar* with absolutely no extra features, as you suggest, then I think we're just wasting our time discussing it. It's not that I'm entirely against sugar, I like the fact that I can do string repetition with "spam"*23 rather than "spam".repeat(23), but (re-iterating what I said earlier) all the important and obvious flavours of sugar have already been done. Adding more will give you source code diabetes. If we've missed one or two use-cases, perhaps we can add operator support for those, e.g. "spam" - "eggs" (whatever that might mean!), without needing to over-generalise the concept to support arbitrary operators everywhere. -- Steven
On Tue, Feb 25, 2014 at 12:47 PM, Steven D'Aprano
it might be handy to raise an exception from inside an expression, but I don't see that this maps to your proposal;
It's easy enough to write, anyway. def throw(x): raise x I've used that in a few syntactic demos, but never in production code. ChrisA
From: Steven D'Aprano
On Mon, Feb 24, 2014 at 04:11:19PM -0800, Andrew Barnert wrote:
Every kind of statement in Python would sometimes be useful as an expression. You can always wrap the statement in a function, but it looks ugly--even with Nick's concise lambda proposal. For example, lets say we wrapped with, try/except, and if/else in functions named using, catch, and cond:
data = using(:?.read(), open(path)) buf = catch(:read_file(path), (FileNotFoundError, :'') b = cond(a, :1/a, :NaN)
All of these are possible today,
You must have a different understanding of "possible" than I do, because they all give me syntax errors.
I explicitly said "even with Nick's concise lambda proposal". Have you worked out an implementation for that proposal and patched your compiler? If not, yeah, you will get syntax errors. Without that proposal, they're even worse, hence the "even with…": data = using(lambda f: f.read(), open(path)) buf = catch(lambda: read_file(path), (FileNotFoundError, lambda: '') b = cond(a, lambda: 1/a, lambda: NaN)
If you're trying to
sell the idea of one new controversial feature, you really shouldn't demonstrate it with another controversial new syntax.
As I already explained, I don't think the infix functions are worthwhile without some fix for lambdas being too obtrusive—whether Nick's or a different one. So, should I just pretend that problem already had a solution and ignore it?
Let me try to make your argument for you, I hope fairly. You're hoping
that by introducing custom infix binary operators, you can replace syntax for (say)
result = 23 if condition else 42
with
result = 23 `if_operator` condition, 42
or something similar.
The fact that ternary if already exists isn't
important here -- it is your argument that if we had custom infix operators, we wouldn't have needed to add ternary if.
I don't think this suggestion comes even close to working well. Without
proper support interpreter support for ternary operators, forcing three arguments around a binary operator is never going to look right, and may have precedence issues.
You say you're trying to be fair here, but then why have you ignored the two cases that are binary (which I listed first), and then rearranged the third case to make it harder to parse and to read? Yes, the if expression we ended up with is also out-of-order. But I wrote the function in the order of the if statement, the ternary if expression in most other languages, and the initial proposals that everyone wanted until nobody could get the syntax right. And that makes it a lot clearer: it's a binary operator between a condition and a tuple of then/else values. (Sure, that makes it read more like a two-case indexed switch than an if, but it's still a lot better than the way you wrote it.)
And it lacks support for lazy evaluation,
I've mentioned from the start, and repeatedly, that this relies on some other solution to that problem, like Nick's concise lambdas. I don't know how else I can say it at this point that will stop someone from saying, "But that doesn't solve lazy evaluation."
None of which are as good as the syntax we have for ternary if.
Which, again, I explicitly said multiple times, including in the very section of the message you're replying to.
This suggests that dedicated syntax beats custom syntax.
Yes. That's almost _universally_ true. But that doesn't mean you should create dedicated syntax for every kind of expression anyone ever dreams up. That way lies madness, or at least Lisp. The bar is very high for what deserves dedicated syntax. The except expression may just barely pass it, but the verdict is still out; the with expression doesn't seem likely to clear; some expression that you need for your own project like piping to an Erlang-style channel or binding two dataflow variables definitely won't.
I'll be honest, it seems to be *so grossly and fundamentally unworkable*
to me that I'm kind of astonished that you've raised it not once but twice, instead of discarding it as ridiculous, like the idea of a paper submarine or a hot iron balloon. Perhaps there are other requirements needed to make this work that you haven't mentioned and I haven't thought of? Sorry to be so dismissive, but that's the truth of it.
If you want me to be honest, I think you haven't actually read most of what I've mentioned. Otherwise, you wouldn't be repeating things that I said as if they were arguments I'd never considered, or trying to imagine what something might look like directly under an example of what it looks like. Here's another example:
If we've missed one or two use-cases, perhaps we can add operator support for those, e.g. "spam" - "eggs" (whatever that might mean!), without needing to over-generalise the concept to support arbitrary operators everywhere.
Here's what I said in a previous reply to you:
Turning with and except into readable expressions without needing to add new custom syntax for each was my initial motivation. It's possible there are only a handful of such cases, and only one of them is important enough to be worth doing, in which case custom syntax for that one is the best answer.
So, do you really think that I haven't considered the possibility that, if there are only one or two use-cases. dedicated custom syntax for those one or two use cases are a better answer, because I only said that about "one" rather than "one or two"? Or are you just assuming that you know the arguments and don't need to read them?
Andrew Barnert wrote re Nick's concise lambda proposal:
data = using(:?.read(), open(path)) buf = catch(:read_file(path), (FileNotFoundError, :'') b = cond(a, :1/a, :NaN)
This doesn't look like Python to me. It's too cryptic, especially with the '?'. If you think a colon in an except-expression looks too confusing, this would be far worse. Also, this kind of style would make it very easy to make subtle mistakes by getting the colons in the wrong places. It's not quite the same thing as a Lisp macro, because the onus is on the caller to remember which arguments need to be quoted. In Lisp, the macro takes care of that. -- Greg
On Feb 25, 2014, at 15:02, Greg Ewing
Andrew Barnert wrote re Nick's concise lambda proposal:
data = using(:?.read(), open(path)) buf = catch(:read_file(path), (FileNotFoundError, :'') b = cond(a, :1/a, :NaN)
This doesn't look like Python to me. It's too cryptic, especially with the '?'.
Yeah, that was my initial reaction to Nick's suggestion, but it's growing on me. Anyway, there's a separate thread on that, which is stalled while I gather use cases from real code. (The stdlib uses lambdas much less than user-level code does, but the docs are full of them...)
If you think a colon in an except-expression looks too confusing, this would be far worse.
Personally, I have no problem with the colon in the except expression.
Also, this kind of style would make it very easy to make subtle mistakes by getting the colons in the wrong places.
I'm not sure about that. I think in most cases, anywhere you put the colons wrong will be a SyntaxError. And most of the exceptions will be places where you should be using a full-syntax lambda or an out-of-line function anyway (e.g., in a dict mapping names to functions). But until I gather more examples I don't think I can argue that effectively.
It's not quite the same thing as a Lisp macro, because the onus is on the caller to remember which arguments need to be quoted. In Lisp, the macro takes care of that.
Right, it's more like a regular function that takes sexprs, where it's up to the caller to quote them. But it's not even really a parallel with that, because this isn't quoting, it really is explicitly making a function.
On 02/26/2014 12:19 PM, Andrew Barnert wrote:
On Feb 25, 2014, at 15:02, Greg Ewing
wrote: Andrew Barnert wrote re Nick's concise lambda proposal:
data = using(:?.read(), open(path)) buf = catch(:read_file(path), (FileNotFoundError, :'') b = cond(a, :1/a, :NaN)
This doesn't look like Python to me. It's too cryptic, especially with the '?'. Yeah, that was my initial reaction to Nick's suggestion, but it's growing on me. Anyway, there's a separate thread on that, which is stalled while I gather use cases from real code. (The stdlib uses lambdas much less than user-level code does, but the docs are full of them...)
If you think a colon in an except-expression looks too confusing, this would be far worse. Personally, I have no problem with the colon in the except expression.
Also, this kind of style would make it very easy to make subtle mistakes by getting the colons in the wrong places. I'm not sure about that. I think in most cases, anywhere you put the colons wrong will be a SyntaxError. And most of the exceptions will be places where you should be using a full-syntax lambda or an out-of-line function anyway (e.g., in a dict mapping names to functions). But until I gather more examples I don't think I can argue that effectively.
Something I thought about when reading Gregs post is it would be nice if these special expressions, or complex expressions, used some sort of common general rules. They would be more like macro's in that sense, but not completely. But it's not that easy is it? For example if we used an introducer.. "$" before parentheses... $(...). And maybe the first word is a type or keyword, followed by a space. No colon is needed there. $(except ...) $(with ...) $(lambda ...) $(_ ...) # local lambda with no args And ... $(dict ...) dict comprehension $(list ...) list comprehension $(gen ...) generator expression containing yield/yield from It's a bit more verbose which some people may like, and maybe other won't. (?) Having the keyword or type fist is also not always wanted, but it does help self document the expression. The difference between these and functions is that these are free to not evaluate a sub part until it's needed. Functions can't do that. And they aren't really objects either although they may resolve to an object. How the rest of the expression is handles is another question. <shrug> What I like about this concept is it clearly separates out complex expressions and helps to keep the core language cleaner and easier to learn for beginners. Ron
It's not quite the same thing as a Lisp macro, because the onus is on the caller to remember which arguments need to be quoted. In Lisp, the macro takes care of that. Right, it's more like a regular function that takes sexprs, where it's up to the caller to quote them. But it's not even really a parallel with that, because this isn't quoting, it really is explicitly making a function.
Andrew Barnert wrote:
On Feb 25, 2014, at 15:02, Greg Ewing
wrote: Also, this kind of style would make it very easy to make subtle mistakes by getting the colons in the wrong places.
I'm not sure about that. I think in most cases, anywhere you put the colons wrong will be a SyntaxError.
No, what I mean is forgetting to put a colon in front of an expression that should have one, or vice versa. It's related to my next point:
It's not quite the same thing as a Lisp macro, because the onus is on the caller to remember which arguments need to be quoted.
-- Greg
$(_ ...) # local lambda with no args
*haoyi$ sudo pip install macropy*
*haoyi$ python*
*Python 2.7.5 (default, Aug 25 2013, 00:04:04)*
*[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin*
*Type "help", "copyright", "credits" or "license" for more information.*
*>>> import macropy.console*
*0=[]=====> MacroPy Enabled <=====[]=0*
*>>> from macropy.quick_lambda import macros, f, _*
*>>> from random import random>>> random()0.26042432926429704>>>
local_lambda = f[random() + random()] >>>
local_lambda()0.9829393394632971>>> local_lambda()1.4040653196619832*
=)
On Wed, Feb 26, 2014 at 1:52 PM, Greg Ewing
Andrew Barnert wrote:
On Feb 25, 2014, at 15:02, Greg Ewing
wrote: Also, this kind of style would make it very easy to
make subtle mistakes by getting the colons in the wrong places.
I'm not sure about that. I think in most cases, anywhere you put the colons
wrong will be a SyntaxError.
No, what I mean is forgetting to put a colon in front of an expression that should have one, or vice versa. It's related to my next point:
It's not quite the same thing as a Lisp macro, because
the onus is on the caller to remember which arguments need to be quoted.
-- Greg
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Steven D'Aprano writes:
Copula are "linking verbs" like "be", "is", "was" or "becomes".
Under what circumstances would you want to write such operators?
For one, in establishing object identity. "is" *is* an operator in Python.
Any infix binary operator a `op b can be written as a function of two arguments, op(a, b). It's not that there are no use-cases for infix binary operators, but that all the obvious ones (such as string concatenation, comparisons, list repetition, etc.) already exist and the rest can nearly always be easily written as functions.
All the obvious ones (such as binary math operators) also can written as functions: def polymorphically_add_things(x, y): return x + y We know where that leads to (hi, John McC!). Obviously, that's not logic you're willing to follow to the bitter end! As for use cases, how about DSLs: @make_named_operator def to(x, y): return (x, y) # There really oughtta be a Graph class with an appropriate repr. tricycle = [ 'a' `to 'b', 'b' `to 'c', 'c' `to 'a'] whine("But, Daddy!!!! I wanna PONEEEEEEEE!!") (Sorry, bad cold, can't remember any use of graph theory in Monty Python.) As I said, I dunno if easy creation of DSLs is desirable. And of course it's a toy example, but I don't think it's fair to hold that against me; a real example would take up a lot of space without being any more convincing, I suspect.
On Feb 23, 2014, at 17:35, Steven D'Aprano
On Sun, Feb 23, 2014 at 12:47:46PM -0800, Andrew Barnert wrote:
Of course this is me talking about Bruce's idea. I don't want to put words in his mouth. And I also don't want people to forget that _my_ idea was using infix functions for _non-mathematical_ cases, and this is only a minor sideline to me.
I can't think of many useful examples of infix operators that aren't mathematical, and even fewer that aren't just as easily written as methods or functions.
Turning with and except into readable expressions without needing to add new custom syntax for each was my initial motivation. It's possible there are only a handful of such cases, and only one of them is important enough to be worth doing, in which case custom syntax for that one is the best answer. But given that half the proposals on this list are for some form of new expression syntax, I'm not sure that's the case.
Please read to the end before responding. I don't know how to write this without sounding hostile toward Windows ... I am. But I'm not hostile toward programmers who develop in Windows! Bear with me. Paul Moore writes:
1. The Windows console is not Unicode-friendly and displaying non-ASCII source code is still problematic.
And there's no IDE (IDLE? but ISTR tkinter breaks on Windows :-( ) or similar that provides a simple Unicode-capable console you can run Python in? AFAIK the Emacs console emulations work fine on Windows (but I haven't tried Python in them). I don't know if Windows shells like Emacs very much (although Emacs does provide a shell written directly in Lisp, so you can circumvent the Windows console and shell completely).
2. Encoding detection is *not* a solved problem, and it's certainly not universally implemented in editors.
I don't understand how this is related. Encoding detection for Python source *is* a solved problem in Python, since PEP 263. I don't really understand why the fact that it's not implemented in *your* editor should be anybody else's problem: Emacs on Windows has been capable (with a patch) since ~1991 (and without a patch since ~1998), due to the fact that PEP 263 coding cookies were modeled after the popular Emacs/vi cookies.
3. Tools like grep may or may not work with extended characters
Of course they do; all the usual stream-oriented Unix tools work fine. It's trivial due to the traditional restriction of tool languages to ASCII and the use of ASCII-compatible encodings. You do need to make sure that any tool arguments (such as regexps) use the same encoding as the stream to be processed (but I suppose that goes without saying). So if you're seeing wierdness, either arguments don't match stream (which often would be due to a console issue) or the console can't input/display the characters correctly.
But it does mean that Andrew Barnert's comment "in the far off day that Python and source editors could handle Unicode. And we have pretty much reached that day." may not be true unless you're willing to ignore Windows users.
Andrew's comment is *precisely* true: Python loves Unicode, and Notepad handles UTF-8 fine. I would imagine any real source editor on Windows does too. So your point comes down to "the Windows console sucks and I don't like Emacs". OK, that's a valid concern -- there are an awful lot of developers for whom the Windows console is an essential component of their environments, and Emacs is more than an acquired taste, it's a different way of seeing the world. But how many more decades is the Windows console going to be allowed to hold back the rest of the world? If it hasn't learned Unicode by now, is there any reason to suppose it ever will? If it won't learn, why not bite the bullet now? There are fewer Windows programmers to feel pain now than there ever will be again (we hope :-)!
(I did read to the end, and I don't really disagree with your points,
even as a Windows programmer :-))
tl; dr - I agree with you but unfortunately Windows isn't dead yet and
practicality means we can't ignore it.
On 24 February 2014 00:45, Stephen J. Turnbull
So your point comes down to "the Windows console sucks and I don't like Emacs". OK, that's a valid concern -- there are an awful lot of developers for whom the Windows console is an essential component of their environments, and Emacs is more than an acquired taste, it's a different way of seeing the world.
Essentially, yes :-) Although there's a bit more to it: * The C language (and hence all programmers') assumption that the basic abstraction for calling a progcess uses argc/atgv. That's not true on some operating systems (Windows, yeah, but "all the world is Unix" is a bad assumption even when the alternative is not Windows) and the C runtime has to jump through hoops (sometimes not perfectly) to pretend that lie is true. (That's the grep issue). I'll take your word re Emacs, as I use Vim. Vim is certainly as capable of Emacs of dealing with Unicode, but it doesn't do so out of the box (it's what happens if you *don't* think hard about Unicode that's relevant to this thread). Probably the most likely thing for people to forget is using the right encoding for Python files (for extra credit, make that UTF8 on Py3, but ASCII on Py2!), even though that differs from the system default.
But how many more decades is the Windows console going to be allowed to hold back the rest of the world? If it hasn't learned Unicode by now, is there any reason to suppose it ever will? If it won't learn, why not bite the bullet now? There are fewer Windows programmers to feel pain now than there ever will be again (we hope :-)!
I wish I knew. I would kill for a proper Unicode-conforming console. But my company provides me with a Windows PC ro use at work, and the games I like to play are Windows-only. So for work and play, Windows is my environment, for better or worse. Maybe Windows 8 is so bad that the climate will shift. Maybe macs will stop costing more than my house at some point. But it's still some way off (and that was the original point - not that Unicode is desirable, but that we've reached the point where using it is not an issue). I would love to switch from Windows, but if I did so for my hobby coding, it'd lose any relevance it has to the rest of my life... Anyhow, we're way off topic here, so I'll stop. Paul
On Mon, Feb 24, 2014 at 7:03 PM, Paul Moore
But my company provides me with a Windows PC ro use at work, and the games I like to play are Windows-only. So for work and play, Windows is my environment, for better or worse. Maybe Windows 8 is so bad that the climate will shift. Maybe macs will stop costing more than my house at some point.
This is where virtualization wins big. And possibly Wine as well.
Maybe both at once - on your Windows machine, create a VM running
Linux, and run your Windows program under Wine in that VM... okay,
maybe that's just a leeedle bit stupid. But it does have its benefits
(like ease of isolating registry changes - I've never seen *any*
pure-Windows solution that's as easy as "create a new WINEPREFIX", and
doubt I ever will). However...
On Mon, Feb 24, 2014 at 11:45 AM, Stephen J. Turnbull
Andrew's comment is *precisely* true: Python loves Unicode, and Notepad handles UTF-8 fine. I would imagine any real source editor on Windows does too.
... Notepad has enough flaws (like its handling of newlines and its pesky BOM header) that I wouldn't recommend it for anything, no matter how good its Unicode in other areas. Yes, it managed to save and load a non-BMP string in such a way that Python managed to retrieve it (although I couldn't find a font that would display it correctly, but that's not a Notepad problem), but unless you're really careful to make your files Notepad-friendly, I wouldn't recommend its use. There are, however, other good editors; SciTE (and presumably other Scintilla-derivatives, like Notepad++) works fine, and doesn't have Notepad's other flaws. So, I'd agree more with the second half of that: any real source editor will be fine, on Windows or any other platform. ChrisA
On Sat, Feb 22, 2014 at 10:17 PM, Bruce Leban
I'm not sure about enclosing the operator character in quotes. C++ and C# use an operator keyword, while Ruby doesn't mark it at all. Python, of course, uses __oper__ functions for overloading built-in operators. Writing __∩__ would complicate the tokenizer as it would have to recognize this exact syntax and treat it as an identifier while not allowing math symbol characters in other uses in an identifier.
What about __intersection__, __union__, etc. It looks like all symbols of interest have relatively short one-two word names in Unicode that can me mechanically converted to dunder method names.
Bruce Leban wrote:
(2) Extended operators can be declared like this.
class sample: def '∩'(self, rhs): return self.value.intersection(sample.get_set(rhs))
Or def __INTERSECTION__(self, rhs): ... def __rINTERSECTION__(self, rhs): ... (i.e. use the Unicode character names.) -- Greg
On Sat, Feb 22, 2014, at 22:17, Bruce Leban wrote:
__∩__ would complicate the tokenizer as it would have to recognize this exact syntax and treat it as an identifier while not allowing math symbol characters in other uses in an identifier. And I probably need to write sample['∩'] not sample.∩. Without the quotes or something along those lines, these look too similar:
def U(a, b): pass def ∪(a, b): pass
def a ∪ b: pass
On Feb 22, 2014, at 17:23, Steven D'Aprano
We could even define that the characters in the combined operator define precendece so the above could be written as
Who is "we"? The programmer of the module, or the Python core devs? In other words, are the precedences set once, in the language, or in each module that uses them?
His point was that if we make precedence programmatic and rule-based, there is no "we", just the fixed and documented set of rules that automatically give you the precedence for any symbol-string operator. Which solves the problem you're asking about.
Am 2014-02-21 23:05, schrieb Andrew Barnert:
While we're discussing crazy ideas inspired by a combination of a long-abandoned PEP and Haskell idioms (see the implicit lambda thread), here's another: arbitrary infix operators:
a `foo` b == foo(a, b)
If you really want to you could do this: class infix(object): __slots__ = '__call__', def __init__(self,func): self.__call__ = func def __ror__(self,arg1): return infix2(self.__call__, arg1) class infix2(object): __slots__ = 'func', 'arg1' def __init__(self,func,arg1): self.func = func self.arg1 = arg1 def __call__(*args,**kwargs): self, args = args[0], args[1:] return self.func(self.arg1,*args,**kwargs) def __or__(self,arg2): return self.func(self.arg1,arg2) @infix def foo(a,b): return a + b print "egg" |foo| "spam"
You should contribute that to PyExt(https://github.com/kirbyfan64/pyext). I'd love to have it there. On Fri, Feb 21, 2014 at 7:45 PM, Mathias Panzenböck < grosser.meister.morti@gmx.net> wrote:
Am 2014-02-21 23:05, schrieb Andrew Barnert:
While we're discussing crazy ideas inspired by a combination of a
long-abandoned PEP and Haskell idioms (see the implicit lambda thread), here's another: arbitrary infix operators:
a `foo` b == foo(a, b)
If you really want to you could do this:
class infix(object): __slots__ = '__call__',
def __init__(self,func): self.__call__ = func
def __ror__(self,arg1): return infix2(self.__call__, arg1)
class infix2(object): __slots__ = 'func', 'arg1'
def __init__(self,func,arg1): self.func = func self.arg1 = arg1
def __call__(*args,**kwargs): self, args = args[0], args[1:] return self.func(self.arg1,*args,**kwargs)
def __or__(self,arg2): return self.func(self.arg1,arg2)
@infix def foo(a,b): return a + b
print "egg" |foo| "spam"
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated."
From: Mathias Panzenböck
Am 2014-02-21 23:05, schrieb Andrew Barnert:
While we're discussing crazy ideas inspired by a combination of a long-abandoned PEP and Haskell idioms (see the implicit lambda thread), here's another: arbitrary infix operators:
a `foo` b == foo(a, b)
If you really want to you could do this:
This is basically the same trick you use in Haskell to allow arbitrary expressions rather than just identifiers to be used infix. Which I don't think you actually want to allow. Also, it requires you to declare certain functions as infix-able rather than just use any functions arbitrarily. Of course that does allow you to give the infix version a different name than the prefix version, like bar = infix(foo), or even bar = infix(partial(Spam(2).long_method_name, 'eggs')), which could I suppose be useful. One question about the implementation:
class infix2(object): __slots__ = 'func', 'arg1' def __init__(self,func,arg1): self.func = func self.arg1 = arg1 def __call__(*args,**kwargs): self, args = args[0], args[1:] return self.func(self.arg1,*args,**kwargs)
First, why not just: def __call__(self, *args, **kwargs): return self.func(self.arg1, *args, **kwargs) More importantly, I assume this is to allow auto-partialing, so "spam" |foo is a normal function of one argument that you can pass around? That doesn't actually work the same way as Haskell operators, and it looks pretty weird in Python too, and I can't think of when you'd want it, but it is clever.
Am 2014-02-22 03:16, schrieb Andrew Barnert:
From: Mathias Panzenböck
Sent: Friday, February 21, 2014 5:45 PM Am 2014-02-21 23:05, schrieb Andrew Barnert:
While we're discussing crazy ideas inspired by a combination of a long-abandoned PEP and Haskell idioms (see the implicit lambda thread), here's another: arbitrary infix operators:
a `foo` b == foo(a, b)
If you really want to you could do this:
This is basically the same trick you use in Haskell to allow arbitrary expressions rather than just identifiers to be used infix. Which I don't think you actually want to allow. Also, it requires you to declare certain functions as infix-able rather than just use any functions arbitrarily. Of course that does allow you to give the infix version a different name than the prefix version, like bar = infix(foo), or even bar = infix(partial(Spam(2).long_method_name, 'eggs')), which could I suppose be useful.
One question about the implementation:
class infix2(object): __slots__ = 'func', 'arg1'
def __init__(self,func,arg1): self.func = func self.arg1 = arg1
def __call__(*args,**kwargs): self, args = args[0], args[1:] return self.func(self.arg1,*args,**kwargs)
First, why not just:
def __call__(self, *args, **kwargs): return self.func(self.arg1, *args, **kwargs)
Because then you can never pass a keyword argument named "self". Exotic case, but still.
More importantly, I assume this is to allow auto-partialing, so "spam" |foo is a normal function of one argument that you can pass around? That doesn't actually work the same way as Haskell operators, and it looks pretty weird in Python too, and I can't think of when you'd want it, but it is clever.
Yeah, I just thought maybe one would like to have that. No special thoughts there. Also this is not my idea. I saw such code somewhere and wrote this down from memory. It could even be that I saw this on this mailing list. Btw. bugfix for the infix class: class infix(object): __slots__ = 'func', def __init__(self,func): self.func = func def __ror__(self,arg1): return infix2(self.func, arg1) def __call__(*args,**kwargs): self, args = args[0], args[1:] return self.func(*args,**kwargs)
Mathias Panzenböck wrote:
Also this is not my idea. I saw such code somewhere and wrote this down from memory. It could even be that I saw this on this mailing list.
The first time I saw this was at http://code.activestate.com/recipes/384122-infix-operators/
participants (20)
-
Alexander Belopolsky
-
Andrew Barnert
-
Bruce Leban
-
Chris Angelico
-
Chris Rebert
-
Greg Ewing
-
Hannu Krosing
-
Haoyi Li
-
Mark Lawrence
-
Mathias Panzenböck
-
Nathan Schneider
-
Nicholas Cole
-
Nick Coghlan
-
Paul Moore
-
Peter Otten
-
random832@fastmail.us
-
Ron Adam
-
Ryan Gonzalez
-
Stephen J. Turnbull
-
Steven D'Aprano