122 comments

[ 6.0 ms ] story [ 185 ms ] thread
I loathe function calls without parentheses; it always looks vaguely ambiguous to me.
i took me a while to get used to `print()` but now that i'm used to it, when i see py2 code i get... confused? it doesn't look right.

same with `class Foo(object):`

> same with `class Foo(object):`

That's still legal, and actually I prefer it even though it's not required in Python 3, because explicit is better than implicit.

> I loathe function calls without parentheses; it always looks vaguely ambiguous to me.

But then again, throwing and catching exceptions through arbitrary depths along the call stack is perfectly clear and neat.

Parentheses are only needed with infix notation. Pure prefix (like Unlambda) or pure postfix notation (like Forth) are unambiguous without them. Whether the resulting language is easy to understand is a far more complicated question.
> Whether the resulting language is easy to understand is a far more complicated question.

That's why I hate them. A programming language is primarily meant to be read by people. If I see something like

    thing noun object
What's going on there? Is thing a function? Is noun? Am I passing two arguments to thing, or am I passing the result of invoking the function noun with the argument object?

Hell, if I have something like

    verb action
it certainly looks like I'm calling a function called verb with the argument action, but is the argument I'm giving to verb the actual function action, or the result of evaluating action?

Yes, in any given language there's probably a single answer to all of these, but I bet it varies from language to language, and I'd really, really, really rather not waste calories by having my brain try to figure it out.

This sounds like a bad idea. It creates more ambiguities in the language, and will require people to remember a lot more. It works in ruby because in ruby you can't assign a function to a variable like

A = len

But you can in python.

It seems python is adding more and more implicit stuff in every new release.

I think the day we can import braces from __future__ might not be far away.

Python becoming Tcl
Yup, this looks a lot like Tcl! But Tcl lets you compose code at runtime using its string lists. I doubt Python will. So why introduce another redundant syntax?
Unfortunately Python will not get the strength of TCL such as metaprogramming with this change.
I would more like to support some kind of pipes, eg.:

123 | add10 | str | print

UPD: Some of my workarounds (dont try this at home lol): [twitter thread] https://twitter.com/tandavaya/status/1155925017848242176

Looks like forth (uses stack instead of pipes though). The first line will define add10 (which is assumed to exist in your example already, but I wanted to show how easy it was to create). 123 puts 123 on the stack. add10 adds ten to the number on the stack. "." prints (and removes) the top of the stack. Don't have to convert it to a string in this case.

    : add10 10 +
    123 add10 .
Take a look at marcel: https://github.com/geophile/marcel. It is a pipe-objects-instead-of-strings shell, implemented in Python. But it is also an API allowing scripting from inside Python, e.g.

    for file, size in ls('/home/jao') | map(lambda f: (f, f.size)):
        print(f'{file.name}: {size}')
+1! I don't like your suggested syntax, but definitely, something like that would be a great addition to Python. Especially if it could be made to work with lists. Rough idea:

    range(10) | @ + 2 | @ % 2 == 0 | print(@)
I’ve used this in production:

  def thread_first(current_value, args):
    for funcargs in args:
        if isinstance(funcargs, list):
            func, *restargs = funcargs
        else:
            func = funcargs
            restargs = []
        current_value = func(current_value, *restargs)

        if current_value is None:
            break
    return current_value

It’s modelled after clojure’s ‘->’. Your example would be

  thread_first(123, [add10, str, print])
So, is this just a syntax change or is this adding a new statement?

If it's the latter, does that mean that the execution environment is part of the language itself, instead of, for example, C where environmental stuff is only accesible through the standard library and the language per se is little more than a context-free grammar?

>does that mean that the execution environment is part of the language itself

Yes, that has always be the case with Python in my understanding - there are lots of built-ins...

> is this just a syntax change or is this adding a new statement?

It's just a syntax change; "print 1 2 3" is still an expression just like "print(1, 2, 3)".

Elixir has this. And now the linter puts parentheses back IIRC because of syntax ambiguity to the human.
Seems to violate "There should be one-- and preferably only one --obvious way to do it."
Says the line with two different hyphenating styles.

It's an Easter egg.

So pedantic but I love it. For the GP, what you are looking for is them em dash (—) with no spaces around it, because the dash is its own form of punctuation.
A double-dash is a perfectly valid substitute for an em dash given that most (no?) keyboards have it on a key. Seems like a waste of time to always have to look up an escape code or copy/paste from somewhere when the intent of "--" is clear.
It’s option-shift-hyphen on a Mac. And you can get at em dashes on an iOS keyboard by long pressing the hyphen. Not so difficult.
Compose--- on anything with a compose key. Linux mostly and Windows with the WinCompose program.
I don't hold random comments on the internet to the highest standard because I am not a monster. However they are not equivalent and the em dash is a sign of extra polish.
Some style guides allow for surrounding the em dash with spaces. It's a matter of preference.
These look like brackets, not binary operators. So, that's one style, but a rather unobvious one, and also contradicts PEP8, which says (as the very first thing in the section on inline spaces!) to avoid whitespace immediately inside brackets.
Sorry, but I think this is a poisonous, thought-terminating cliche.

Python has had multiple ways of doing many things for a very long time, and the longer you program in any language, the more you realize there are often many solutions to a problem and the "best" approach either depends on context or you realize there is no one "best" approach and go on preference.

I think the idea should be judged on its own merits without having to consult the Zen. That said, I seriously don't see the point of it. Python 3 already forced everybody to convert all their print statements to look like function calls, and now GVR wants to bring the old style back as an option? It's just parenthesis, what's the point?

If he wants to put that new parser to work, how about taking another look at multi-line lambdas?

The key words are ‘preferably’ and ‘obvious’ (although that way may not be obvious at first unless you're Dutch). It's fine if there are merely multiple ways to do something, as long as you're not actually giving people tough choices.
> Python has had multiple ways of doing many things for a very long time ... the more you realise there are often many solutions to a problem

But "solution" and "ways of doing things" from a language perspective are different. You can one way of doing something in a language, yet provide different implementations of the same solution. The difference is how different a "way" is at some level of abstract (vs "syntactic sugar).

Inventing new sentences is needed everyday, inventing new words much rarely so. "programmable program languages" or those highly dependant on custom frameworks fall prey to this: where code becomes a personal space that is hard to break into. You have to "terminate-thought" at some level, if else everyone "creatively" reinvents the low level (e.g. built-ins) then no collaboration is practical; accept the standard building block, and implement your solution with those.

> there is no one "best" approach

but there are "better" approaches

"There should be one-- and preferably only one --obvious way to do it."

This principle is often repeated for Python, but does it have any bearing on reality today? With every release of Python (and indeed for many other languages), new features give you even more ways of doing things.

Python is 30 years old - it's no longer the small language it may once have been, but is now chock-full of features. Maybe once there was an "obvious way to do it", but I don't thing this is true today.

Surely this principle becomes more important the more stuff you put in?
> Seems to violate "There should be one-- and preferably only one --obvious way to do it."

Just as representing a vector of numbers as a tuple, a list, a dictionary, a numpy matrix or a numpy array. And don't get me started on multidimensional arrays.

The only place I've ever liked function calls without parentheses is Lua. But it only supports it with a single literal string or table argument.

So, boo. Hiss.

OK, so I sort of get it. I love ML style syntax. I love the way that it supports great things like partial application.

What I do not like is TMTOWTDI. And I do not like Python violating its own principles (namely, "explicit is better than implicit"). As far as I am concerned, in Python, which does not have partial application, function application is handled by a special `(...)` operator that explicitly applies whatever function comes before the parens to whatever is inside the parens. Making the parens optional gives you two different ways to apply a function - and, by extension, one more place for people to argue about style - one of which is just an implicit version of the other.

You've got to have a way better reason to do that to a language than just, "Hey, isn't this cool? And also, I miss the Python 2 vs Python 3 wars, so wouldn't it be fun to give that pot another stir for old time's sake?"

I get it, the change from a print statement to a print function caused some pain. But it did bring some practical benefits, and the transition is in the past now, and, to quote the Zen of Python again, "special cases aren't special enough to break the rules."

This is on point.

I'm also going to throw another reference to PEP-20 in the mix here: "There should be one-- and preferably only one --obvious way to do it." This implies either forcing parens everywhere or not using them at all. Paired with "explicit is better than implicit", I'd say that points pretty clearly at using parens everywhere.

Another thought: This would create ambiguities in the language that would harm its readability. Maybe not as far as the computer is concerned, but certainly as far as the humans are concerned. Is

  print(1, 2, 3)
supposed to print

  1 2 3
or

  (1, 2, 3)
?

What about

  print (1, 2, 3)
?

Are they the same? Should they be different? I know what the answer is in Python 3.8. A few weeks or months of programming in a Python where parens are optional, though, and I wouldn't be so sure anymore.

This is explicitely explained in the linked article.

To solve the conflict the first parameter cannot start with a paren.

By, "Maybe not as far as the computer is concerned, but certainly as far as the humans are concerned.", I meant to invoke the principle that there's more to a good grammar than the computer being able to parse it reliably. It's also got to not be confusing for its wet, squishy readers.
> You've got to have a way better reason to do that to a language than just, "Hey, isn't this cool? And also, I miss the Python 2 vs Python 3 wars, so wouldn't it be fun to give that pot another stir for old time's sake?"

That was my first thought. Why would we turn a simple, binary choice -- "< 3: statement, >= 3: function" into a big old... maybe?

The binary choice hurts, because it dictates changes. Sure. People dislike change, but it's easy to support.

However, there's one thing worse than change, and that's inconsistency and uncertainty. like, in this case, going "zoinks, your change wasn't actually necessary!". Except, now we have both choices. So it'd be even more confusing.

As they say, the road to Perl is paved with good intentions.

There are several languages that people like for this sort of flexibility, but I think Python's relative rigidity has always been one of its strengths.

I don't see why they couldn't just leave the old print statement in place so it wouldn't break as much code as it did.
It was an inconsistent albatross of a special case. The whole point of 3 was to break things that were bad ideas.
So what? In the history of Python, there was literally 0 bugs caused by print being a statement. Breaking backwards compatibility for shit like that is amateur hour.

edit: And just to be clear, I'm fine with making print a function for all new code. There were ways that the python community could have accomplished that [and the unicode switch] without breaking the entire language.

Being a statement is less of an issue than the special behavior of the trailing comma, '>>' token, and the inability to augment behavior with named arguments. The lexer shouldn't have to deal with that sort of stuff.
> The lexer shouldn't have to deal with that sort of stuff.

The problem with that is that the entire python ecosystem had to deal with that instead...

I'm going way past rant territory at this point, but there's a reason that Microsoft and Amazon are worth a trillion dollars a piece, and it's not because of beautiful and elegant APIs.

The whole ecosystem had over ten years to import print_function and fix their shit for the eventual flag day.
Python 3 literally broke "hello, world". For some vague notion of aesthetics. Or for some even more obscure "inside baseball" reason that 99% of Python users could care less about.

The following still compiles and runs, on the compiler that comes with the latest OpenBSD:

    main()
    {
        printf("hello, world\n");
    }
Somehow the C language has managed to survive and thrive without breaking the canonical example program.
It survived and thrived because it did not break the canonical example program. If they had broken printf in say 1995 the situation would be entirely different today. Oh, and they did get it right the first time by making it a function.
SO, you're saying that if Python abandoned quality in favour of aggressive sales and marketing in the channel and cut-throat monopolistic practices against competing program languages, then its core programmers would rise to the ranks of American social elites?
Alright, I'll bite. Why do you think not breaking users programs is abandoning quality?
Backwards compatibility is something that you only ditch for extremely good reasons, for instance to deal with a security issue that can not be handled in any other way. Breaking it for reasons of aesthetics is just plain dumb. The python ecosystem is still suffering from these major mistakes many years later and python's image as a reliable and trustworthy tool suffered right along with it. Such changes are fine pre 1.0, but once you ask people to base their production systems on it you maintain backwards compatibility at a cost, even if that means holding your nose occasionally. And my suggested fix would at least allow for the old syntax to live happily along with the new one, which given the price (two stupid brackets) could have easily been dealt with. Focusing on purity vs the responsibility towards people maintaining 100's of millions of lines of code in aggregate (if not another order of magnitude more) means you don't have your priorities straight.

Imagine we'd drop HTTPS for a new protocol because it fixes the 'Referrer' spelling error.

The analogy here is: "Imagine making a new, incompatible protocol and NOT fixing the 'Referrer' spelling error, because the old protocol had that spelling error"

Python 3 is the version with breaking changes from 2.x and while stuff is being broken, they decided to fix the print statement.

Python 3 wasn't invented to fix the print statement.

The backwards compatibility was already ditched. It's over.
I don't think having two different syntactic ways to write the the same thing - at a purely syntactic level - is a violation of TMTOWTDI. After all, Python already lets you write:

    f(a)
    f((a))
    (f)(a)
    (f)((a))
etc. and those all mean the same thing, and no-one finds this confusing.
> which does not have partial application functools.partial?
Agreed

There is no reason to not require parenthesis. No reason.

This is what I hate about Ruby, not having parenthesis gets a bit confusing and adds a couple of gotchas

Print statement is no more. It is gone. It went to meet its maker. Forget about it

Doing away with parenthesis is not syntactic sugar, it's the watermelon seeds that get in the way

Method call without parenthesis is very useful for making DSL.
If Python wants to come closer to a functional style of programming, then they should allow inline lambda functions which span multiple lines and still follow the normal syntax (i.e., escaping newlines would be cheating).
I really hope they do not do this. In and of itself it is not a problem, Ruby code is perfectly readable when the style is consistent.

Software developers are often stubborn. The main result of this change will be a mess of code which follows a mix of all the styles. That removes one of the greatest beauties of the Python ecosystem, that there is a "right way" to do things.

Agreed. I deeply love the concept of Pythonic.

The Pythonic way to do it is (very) often not my favorite way to do it. But, if I'm working in Python, I'm going to do it that way, anyway. Because social factors matter, and choosing your own convention rarely yields enough benefit to justify choosing not to follow the convention.

> but I'll withdraw it if the response is a resounding "boo, hiss"

Boo, hiss.

The parentheses-less function call is the second biggest obstacle to ruby readability.

This is more Ruby than Python. I specifically prefer Python more because of its explicitness.
If you want to see some nice examples of PEP 617's (The new PEG Parser) potential, see the draft of https://www.python.org/dev/peps/pep-0622/

But `print 'hi world'` This would really spoil one of the nice consistency things python 3 brought.

Callables without parenthesis to distinguish the arguments. Can you imagine what this is going to do to readability on open source projects?

This was back in June, and probably for the sake of conversation. It got my (and a lot of others) attention, though. If that was the case, mission accomplished.

I'm all for the new parser enabled in Python 3.9 (and switched on by default in 3.10): https://www.python.org/dev/peps/pep-0617/

What is the point? Why is this a desirable thing at all? TFA goes directly into implementation and has not a word about why this is desirable for readability, aesthetics, or any other criterion.

Seems like the sort of faddish syntactic sugar that they love in Swift. Please, not in Python.

I think "the point", given the wording of the email, is to showcase the power of the new parser; not implement it.

(I fucking hope)

This is the exact language Guide stopped being BDFL. STOP IT!
The ambiguity of

foo (boo, hiss)

should immediately close the issue. What the heck is happening?

> I believe there are some other languages that support a similar grammar (Ruby? R? Raku?) but I haven't investigated.

Yes you did. And you want to take the good things from ruby. You want no parentheses calls, block syntax instead of lambdas, chainable map/filter/sort/reduce, accessor syntax, you want to extend basic types so you can call methods on literals. You want ruby all over you. Just admit it.

The optional parentheses for any function Guido proposes is similar to how Nim works. It's pleasant within Nim but seems too TMTOWTDI for Python.
> I believe there are some other languages that support a similar grammar (Ruby? R? Raku?) but I haven't investigated.

I'm amazed that Guido is not aware of Nim which does precisely this. It works brilliantly too. It would be incredible if Python becomes more like Nim (Nim itself having been inspired a fair bit by Python).

(comment deleted)
Too ambiguous.

One thing I love about Python is that it's clear when I'm passing a function/method as argument, or aliasing a class, or just generally knowing when I'm calling something vs referencing it.

This is a no-go from the start.

I do not like this idea. I think it is better to stick to one standard of syntax and do not create several ways of doing same thing without added significant benefit.
I seriously do not see any worthwhile benefit in this, and significant cost.

Benefits: You can use spaces instead of parenthesis. It's the same number of characters and function arguments won't work with spaces.

Costs: Python has to support this forever. Increased complexity. More difficult to read code, because you could be doing the same thing in multiple ways. More style guidelines to enforce. Confusion among new programmers about why they can't use function arguments without parenthesis. Confusion among new programmers about why they would use spaces instead of parenthesis (or the reverse).

Seriously, I do not see any reason why they would ever want to add this in light of the seemingly obvious costs.

> Missing parentheses in call to 'print'. Did you mean print(1, 2, 3)?

Yes, Python, I meant exactly that! I've never seen this error message in error. Now you know what I mean, please fix it automatically, I know you can do that. Heck, throw a single warning if you really want to enforce this. I can ignore warnings that I don't care about.

Yeah, generally please second-guess me Python, like my old pal Clippy did, what could possibly go wrong?
> You're flying! How?

> Python! I learned it last night! Everything is so simple! Hello world is just: print "Hello, world!"

> SyntaxError: Missing parentheses in call to 'print'. Did you mean print("Hello, world!")?

> I dunno... dynamic typing? Whitespace?

> Come join us! Programming is fun again! It's a whole new world up here!

> But how are you flying?

> I just typed: imports antigravity as ag

> TypeError: imports() takes 2 positional arguments but 3 were given

From https://xkcd.com/353/

I'm not sure whether to take this as an in-joke (knowing the reference is part of the fun) or poor attribution (credits should be given where they're due).

Ruby does this and I hate it. It makes it very hard to quickly parse code: you have to ponder every line to identify what is what.

Parenthesis makes very clear what context you are in.

Can't say that I hate it, exactly, but it does make it significantly harder to get into Ruby code when you're not exposed to it daily. Can an amateur woodworker complain their tools are too hard to use if others have no trouble with it and are more productive than with the alternative? Maybe?

So rather than saying that I don't like it, I think what I'd like is some (semi-)objective way of figuring out whether the advantages are worth it. I'm inclined to say it's not worth it, but I don't know.

I don't see any benefits to this, Guido does a good job mentioning many of the negatives .. and, aware of them all, I am surprised he would push on. It seems an unreasonable increase in complexity for no tangible gain.