Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

++ does not throw a SyntaxError #83697

Closed
Marco-Sulla mannequin opened this issue Feb 1, 2020 · 7 comments
Closed

++ does not throw a SyntaxError #83697

Marco-Sulla mannequin opened this issue Feb 1, 2020 · 7 comments
Labels
3.9 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error

Comments

@Marco-Sulla
Copy link
Mannequin

Marco-Sulla mannequin commented Feb 1, 2020

BPO 39516
Nosy @stevendaprano, @ericsnowcurrently, @Marco-Sulla

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2020-02-01.12:21:39.741>
created_at = <Date 2020-02-01.10:48:00.987>
labels = ['interpreter-core', 'type-bug', 'invalid', '3.9']
title = '++ does not throw a SyntaxError'
updated_at = <Date 2020-02-04.19:49:31.024>
user = 'https://github.com/Marco-Sulla'

bugs.python.org fields:

activity = <Date 2020-02-04.19:49:31.024>
actor = 'Marco Sulla'
assignee = 'none'
closed = True
closed_date = <Date 2020-02-01.12:21:39.741>
closer = 'steven.daprano'
components = ['Interpreter Core']
creation = <Date 2020-02-01.10:48:00.987>
creator = 'Marco Sulla'
dependencies = []
files = []
hgrepos = []
issue_num = 39516
keywords = []
message_count = 7.0
messages = ['361159', '361165', '361168', '361171', '361186', '361350', '361364']
nosy_count = 3.0
nosy_names = ['steven.daprano', 'eric.snow', 'Marco Sulla']
pr_nums = []
priority = 'normal'
resolution = 'not a bug'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue39516'
versions = ['Python 3.9']

@Marco-Sulla
Copy link
Mannequin Author

Marco-Sulla mannequin commented Feb 1, 2020

Python 3.9.0a0 (heads/master-dirty:d8ca2354ed, Oct 30 2019, 20:25:01) 
[GCC 9.2.1 20190909] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1 ++ 2
3

This is probably because the interpreter reads:

1 + +2

  1. ++ could be an operator in future. Probably not. Probably never. But you never know.
  2. A space between an unary operator and the object should not be allowed
  3. the first expression is clearly unreadable and hard to understand, so completely unpythonic

@Marco-Sulla Marco-Sulla mannequin added 3.9 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error labels Feb 1, 2020
@stevendaprano
Copy link
Member

This is not a bug, as you have pointed out yourself you are using a binary plus and a unary plus operator in the same expression.

This has been part of Python since version 1, and with operator overloading obj + +thing could mean whatever the objects want the two operators to mean. Unary plus is not necessarily a no-op. For example, the Decimal class gives a meaning to unary plus.

++ should never be an operator in the future, precisely because it already has a meaning today (either two unary pluses, or binary plus followed by unary plus). Even if the compiler could distinguish between the cases, the human reader would not.

The first expression is not "unreadable". The fact that you were able to read it and diagnose it yourself as a binary operator followed by a unary operator proves that you can, in fact, read it. And it probably wasn't that hard to understand. (It wasn't for me.)

It would be easier to read if you used spaces around the binary plus and no space between the unary plus and its operand, but it can still be read and understood even with the unusual spacing.

@Marco-Sulla
Copy link
Mannequin Author

Marco-Sulla mannequin commented Feb 1, 2020

This is not a bug
No one said it's a bug. It's a defect.

This has been part of Python since version 1
There are many things that was part of Python 1 that was removed.

++ should never be an operator in the future, precisely because it already has a meaning today

This is not a "meaning". ++ means nothing. Indeed

>>> 1++
  File "<stdin>", line 1
    1++
      ^
SyntaxError: invalid syntax

The first expression is not "unreadable". The fact that you were able to read it and diagnose it [...]

The fact I understood it it's because I'm a programmer with more than 10 years of experience, mainly in Python. And I discovered this defect by acccident, because I wanted to write a += b and instead I wrote a ++ b. And when happened, I didn't realized why it didn't raised a SyntaxError or, at least, a SyntaxWarning. I had to take some minutes to realize the problem.

So, in my "humble" opinion, it's *highly* unreadable and surprising.

@stevendaprano
Copy link
Member

Marco, this is no more of a defect than x*-y or a&~b. It is a binary
operator followed by an unary operator, just like x--y, x/-y,
x+-y, x**-y etc. Python has at least three unary operators and at
least 17 binary operators so in total there are 51 possible legal
combinations of a binary operator followed by an unary operator.

++ isn't special, it's not unique, or a defect, or a bug.

1++
  ^

SyntaxError: invalid syntax

As a programmer with more than 10 years experience, you should know that
this example is a syntax error because you are missing the right hand
operand, not because ++ has no meaning. You would get precisely the
same syntax error with 2* or 2==.

@Marco-Sulla
Copy link
Mannequin Author

Marco-Sulla mannequin commented Feb 1, 2020

++ isn't special

Indeed the problem is that no error or warning is raised if two operators are consecutive, without a space between. All the cases you listed are terribly unreadable and hardly intelligible.

Anyway I do not agree ++ is not special:

you should know that this example is a syntax error because you are missing the right hand operand, not because ++ has no meaning

But you should know that in a lot of other popular languages, ++ and -- are unary operators, so it's particularly surprisingly to see that they seems to work in Python, even if they seems to be a binary operator.

This is completely confusing and messy. Frankly, I'm not a PEP-8 orthodox at all. I think that you can write a+b. It's not elegant, it's a bit less readable that a + b, but it's not the end of the world.

But you should not be allowed to write a+-b without at least a warning, because +- seems a binary operator. And you should not be able to write a+ -b too, with the interpreter that acts like Ponzio Pilato, because what's this? Is it an unary + or an unary -?
We know the unary is the -, a+ has no sense. but for someone that does not know Python, it's not readable. So, IMHO, the interpreter should at least raise a warning if the syntax is not:
a + -b
for any combination of binary and unary operators.

@ericsnowcurrently
Copy link
Member

FWIW, this is the sort of thing that is usually best suited to be reported by linters, not the Python runtime.

@Marco-Sulla
Copy link
Mannequin Author

Marco-Sulla mannequin commented Feb 4, 2020

this is the sort of thing that is usually best suited to be reported by linters, not the Python runtime.

TL;DR: if you write something like a -- b, it's quite extraordinary that you really wanted to write this. You probably wanted to write a - -b or, more probably, a -= b. So the parser is masking a potential subtle bug, that can cause hours of stressing debugging, because probably the program will run without problem but gives you a wrong result, or will throw an exception but in a completely different point.

Long version:

Normally I agree, but not in this case.

PEP-8 defines line guides for writing a more readable code. They are not mandatory because:

  1. there are cases in which is more readable if you not follow PEP-8 (for example, using \ with long with statements)
  2. there are cases in which the rule is not followed because of using it in fast tests (as for from module import *)
  3. sometimes is simply not possible to follow PEP-8 (for example, many classes can easily implement eq, but implementing all the other comparison operators many times is simply not possible)
  4. sometimes the recommendation can't be followed, because it's not what you want to achive (for example, sometimes you need to check the exact class of an object and use type(a) == SomeClass instead of isinstance(a, SomeClass))
  5. there are cases that PEP-8 does not work. For example, bool(numpy.ndarray) does not work, you must do len(numpy.ndarray)
  6. sometimes, it's simply a matter of style. One prefers a style, another one prefer another style

That said, none of these valid border cases can be applied to this case:

  1. a+-b can be NEVER more readable than a + -b
  2. a++b is clearly faster because you have to write... 2 spaces less. Well, I think that you'll never write a ton of binary operators followed by a unary one, so I suppose two little blank spaces does not slow down you too much :-D
  3. it's always possible to separate a * -b, for example
  4. if you write something like a -- b, it's quite extraordinary that you really wanted to write this. You probably wanted to write a - -b or, more probably, a -= b. So the parser is masking a potential subtle bug, that can cause hours of stressing debugging, because probably the program will run without problem but gives you a wrong result, or will throw an exception but in a completely different point.
  5. See 3
  6. this is IMHO not a matter of style. Writing a ++ b is simply ugly, much unreadable and prone to errors.

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.9 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

2 participants