
Bringing the print statement back - Myrmornis
https://lwn.net/ml/python-ideas/CAP7+vJKsrT-n4rHjaR4PYPRq09JOZcoR54ZjkboOkZcYhB+7oA@mail.gmail.com/
======
mumblemumble
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."

~~~
mumblemumble
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.

~~~
yuchi
This is explicitely explained in the linked article.

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

~~~
mumblemumble
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.

------
zaptheimpaler
Don't worry, its not coming back. Just goes to prove the old rule - no one
reads beyond the headline :)

\----

> Why is this being proposed? > > I think we would need a very strong reason
> to consider this, > and so far I haven't seen any justification other than
> "because > we can". >

(Guidos response below):

There was definitely something of that... I was looking at the new PEG parser
and realized that _if people wanted it_ this would be easy to do. So I spent a
pleasant hour or two coding it up to my satisfaction.

But I was also trying to satisfy some demand. When Python 3 was young, print
becoming a function was one of the most frequent complaints, and it's still
occasionally seen on Twitter. I found at least two StackOverflow issues about
it, but the combined upvote count was less than 100.

An early post in this thread reminded me that IPython has a feature called
"autocall" that allows exactly this syntax. I don't know how popular it is.
However, apparently there the form `f x+1` ends up calling `f("x+1")` (i.e.
stringifying the argument), so introducing similar syntax in Python with
different semantics would hardly be helpful. (If someone wants to start a
debate on argument quoting, please start a new thread, so we can lay this one
to rest.)

All in all, it's clear that there's no future for this idea, and I will
happily withdraw it.

\----

~~~
nurettin
I remember him being present in the github issue tracker before they moved to
bugs.python.org. His presence was next to useless when people were reporting
problems about the async library. What went on was like; he made comments on
his suspicions, someone who actually knows what's going on made a pull request
without even commenting on his ramblings. Again and again for years. Now he's
looking for validation hacking the parser.

------
enitihas
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.

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

Behold, bython:
[https://github.com/mathialo/bython](https://github.com/mathialo/bython)

~~~
readmodifywrite
I love that the example uses the exact same indentation format that Python
already requires.

------
D13Fd
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.

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

~~~
AlexMax
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?

~~~
Veedrac
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.

------
tony
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/](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/](https://www.python.org/dev/peps/pep-0617/)

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

~~~
fernandotakai
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):`

~~~
pdonis
_> 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.

------
cjhanks
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.

~~~
mumblemumble
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.

------
corbet
See also the article
([https://lwn.net/Articles/823292/](https://lwn.net/Articles/823292/)) written
about this idea.

------
geophile
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.

~~~
scrollaway
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)

~~~
kissgyorgy
This is the exact language Guide stopped being BDFL. STOP IT!

------
klyrs
The ambiguity of

foo (boo, hiss)

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

------
coolreader18
Nim does this, and the whitespace is significant[0][1]:

    
    
        echo $foo
        # is parsed as
        echo($foo)
    

[..]

    
    
        echo(1, 2) # pass 1 and 2 to echo
        echo (1, 2) # pass the tuple (1, 2) to echo
    

[..]

    
    
        proc optarg(x: int, y: int = 0): int = x + y
        proc singlearg(x: int): int = 20*x
        
        echo optarg 1, " ", singlearg 2 # prints "1 40"
        
        let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call
        let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments
        let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis
        assert x == y
    
    

[0]: [https://nim-lang.org/docs/manual.html#syntax-precedence](https://nim-
lang.org/docs/manual.html#syntax-precedence)

[1]: [https://nim-lang.org/docs/manual.html#procedures-command-
inv...](https://nim-lang.org/docs/manual.html#procedures-command-invocation-
syntax)

~~~
gen220
Is there a justification for this behavior?

This makes it quite hard to "sight-read" code with reasonably-sized function
names and arg lists:

    
    
        do_some_thing_descriptive (some_arg_2, some_longer_arg)
    

[ ... other code ]

    
    
        do_some_very_descriptive_thing(some_arg_2, some_longer_arg)
    

I imagine that each file would try to remain internally-consistent with
preferring one syntax over the other.

IMO, in scenarios where a module must remain internally-consistent on its one-
of-many-ways to do it, for the sake of legibility, there should be only one
way to do it. Although, I'm open to hearing any good-faith arguments to the
contrary!

------
confeit
> 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.

~~~
coldtea
Yeah, generally please second-guess me Python, like my old pal Clippy did,
what could possibly go wrong?

~~~
confeit
> 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

~~~
lucb1e
From [https://xkcd.com/353/](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).

------
tech2
I remember this kind of thing from days gone by with coffeescript, and the
absolute hell it caused. It may have been made worse by js being callback-
heavy, but assuming I had a function:

    
    
      def bob(cb, *args):
        do some stuff...
    

Then the call:

    
    
      bob somefn, 2, 3
    

Works. But I remember having hellish times tracking down issues when you
missed a single comma.

    
    
      bob somefn 2, 3
    

Now you have bob being passed the result of evaluating the callback (early)
with 2, 3 - and this is discovered at runtime. Ugh!

------
ris
> 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.

------
tandav
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](https://twitter.com/tandavaya/status/1155925017848242176)

~~~
phyrex
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])

------
gilbetron
This is something I hated about Ruby (which is generally a pretty nice
language) - Use parenthesis in your function calls ... or not! It's confusing,
especially when used with other optional things in Ruby - different ways to
pass in named values, for instance. It just confuses for no gain.

------
jeanvalmarc
If they'd done this right at the PY3K transition and _only_ allowed the
function-call-without-parenthesis syntax for `print` calls it could have been
a good idea. But now that the community paid the decade-long price to get
print as a normal function why backtrack?

------
perlgeek
Please not.

Sincerely,

a programmer from a language that allows this.

(To elaborate: Perl and Raku allow this. It's not a bad idea on its own, but
it comes with lots of tradeoffs that, IMHO, don't fit with the design of
Python).

------
haecceity
>>> print (1, 2, 3)

Is that supposed to be a function call with a tuple or function call with 3
integers?

~~~
lucb1e
Well that's the point they're making: it's a downside that you can't call a
function with a tuple as first argument. So it's supposed to be an argument,
hence the space, but the interpreter is blind for that and takes it as 3
arguments. And frankly, I'd say the interpreter is right: anything else would
leak to really freaky bugs that I don't with upon any beginning programmer.
This also kind of makes me conclude that the whole idea is folly, it just
causes ambiguity and doesn't really server any purpose whatsoever.

------
VWWHFSfQ
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.

------
HALtheWise
One really nice feature from Wolfram Mathematica that few other languages seem
to have is a special prefix-only syntax for single-argument function
application. In Mathematica, it's f@x ==> f(x), and it's really convenient
because it prevents needing to move your cursor to the end to put in a closing
paren. Since it only works for single-argument functions, there's no
ambiguity.

~~~
tzot
In any case, this can be done now with a helper class used as a decorator.

    
    
      >>> class At:
        __slots__= 'fun',
        def __init__(self, fun):
          self.fun = fun
        def __matmul__(self, arg):
          return self.fun(arg)
        def __call__(self, *args, **kwargs):
          return self.fun(*args, **kwargs)
    
      >>> @At
      def square(x):
        return x*x
    
      >>> square(2)
      4
      >>> square@2
      4

------
dasb
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?

~~~
pdonis
_> 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)".

------
wwright
This proposal seems _very_ non-composable, by which I mean that the syntax
does not integrate with a number of other features. He says that currently you
can’t nest it in other expressions for example, and you can’t use it with
arguments starting with a parenthesis.

I personally like the aesthetic of it, but the non-composability makes it a UX
mess in my opinion. Languages are a toolset for combining things, and when
those tools just arbitrarily refuse to be combined (or combine irregularly),
you end up bending over backwards to make the language get out of your way
rather than getting a job done.

------
Daishiman
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.

------
gtrubetskoy
Python becoming Tcl

~~~
AlexandrB
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?

------
marczellm
Check out xonsh which is an application of python + that sort of idea to the
problem space of shell scripting.

[https://xon.sh/](https://xon.sh/)

------
beervirus
Allowing this syntax for all functions and methods seems ridiculous,
especially with the problems Guido mentions (like how the first argument can't
begin with a paren or bracket, wtf). But a limited version of this feature
that's specific only to the print statement seems fine. If the parser already
has the complexity to issue a warning like:

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

Then why not just have it DWIM?

------
flingo
This is insane, I love it.

How long until this is a library on pypi?

------
SiVal
return (boo, hiss)

There is something mature and responsible about a language that tends to
refuse a command if the meaning isn't obvious, forcing you to make it obvious.
If instead, you assign meanings to every ambiguous statement, and to the
inevitable exceptions and exceptions to exceptions, you end up with
JavaScript: "Oh, well, too late now"-oriented design.

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

Poor effort tbh but I’ve been excited about new possibilities before too.

Can anyone say which style of parser python used before and why The bee PEG
parser is more powerful. It’s my understanding that PEG parser are equivalent
to RDPs.

------
rerx
I felt that the old print statement sometimes was handy when I wanted to
quickly throw some debugging lines into the code -- just less hassle with
parentheses. But I wouldn't want any of the ambiguity GvR's suggestion would
bring.

For debugging comfort the print statement is half measures anyway: Give us
Julia's @show macro!

------
dom96
> 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).

------
dmitriid
Elixir has this. And now the linter puts parentheses back IIRC because of
syntax ambiguity _to the human_.

------
epicureanideal
This is more Ruby than Python. I specifically prefer Python more because of
its explicitness.

------
war1025
I'd be all for bringing back the print statement since it's the main breaking
change I run into when flipping back and forth between python 2 and 3.

The idea of making parens optional for all function calls seems like just way
too much though

------
ccmcarey
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.

------
imtringued
Groovy has this and it's only really meant to be used for DSLs. Think of
something like build.gradle. You can try to use it everywhere but I feel there
are way too many reasons to not do so.

------
saiojd
I wish all languages just adopted ML syntax for function application.

------
978e4721a
Just add partial application and haskell lambda syntax.

Many think think that python already lost almost everything that it was loved
for initially. So why not change it more drastically

------
BiteCode_dev
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.

~~~
lucb1e
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.

------
vasili111
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.

------
kbd
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.

------
simzor
Hate the idea to be honest. In my opinion this will just bloat the language,
no need. Rather avoid the hundreds of different standards like PHP.

------
eatonphil
When I program in Standard ML or F# I always use parentheses to make it
clearer that a function call is happening.

~~~
shikoba
You shouldn't. It add complexity for the reader. It's always clear what is the
function and what are the arguments.

~~~
eatonphil
I disagree.

The funny thing is that in Standard ML and F# it's less common to pass
arguments solely through currying anyway. The standard libraries in both use
tuples.

I have always felt as a reader that this and camel-case makes code easier to
read.

------
nurettin
> 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.

~~~
lizmat
Or Raku :-)

------
dathinab
Fun, put please don't.

It makes python less consistent as you can not use this in a chain and a view
other positions.

------
Too
What's next? Make quotes around strings optional? Or commas between array
elements?

------
cafard
Is this what Python really needs?

------
enriquto
LOL, I totally love this!

------
aardvark291
Seems a bit early/late for April Fools.

