classification
Title: Proof of concept: implicit call syntax
Type: Stage: resolved
Components: Versions:
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: Ramchandra Apte, alex, ethan.furman, ezio.melotti, gvanrossum, ncoghlan, peter.otten, pitrou, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2013-08-20 04:59 by ncoghlan, last changed 2013-08-21 01:50 by ethan.furman. This issue is now closed.

Files
File name Uploaded Description Edit
implicit_call_statements.diff ncoghlan, 2013-08-20 04:58 Restore print statements as implicit call statements review
Messages (10)
msg195679 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2013-08-20 04:58
After watching one of the presenters at PyTexas struggle painfully with the fact "print value" doesn't work in Python 3, I decided I had to at least *try* to see if a general "implicit call" syntax was feasible within the constraints of the CPython parser.

The attached patch shows that it is, indeed, possible to implement such a syntax by modifying the definition of expr_stmt to allow for an implicit trailing argument list (using the normal argument syntax).

The key benefit of such a feature is that the following would once again be legal Python syntax:

    print "Hello world!"

Ambiguous cases (such as "expr * expr" and "expr ** expr") obey the rule that implicit calls are very *low* precedence, so you have to use parens to force the call interpretation. Similarly, a bare expression is not call - you must still append () to force a call without arguments.

In addition to being a pain at the interactive prompt, the change of print from a statement to a function has also been noted as one of the biggest problems with compatibility of third party user scripts for Linux distros changing the default system Python to Python 3. This change would mean most Python 2 print statements either do nothing (no arguments), produce an unexpected trailing newline (trailing comma) or work exactly as they do in Python 2. Only those that use the Python 2 redirection syntax continue to trigger a syntax error in Python 3.

While the *code* changes to achieve this behaviour turned out to be relatively small (most of the diff is in the autogenerated parser code), the documentation and general pedagogical impact could be substantially larger.

If there's even an outside chance this could be accepted, I'm happy to work up a full PEP for it.
msg195681 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-08-20 08:25
> In addition to being a pain at the interactive prompt

Just type "pr<TAB>" and the "print(" call will be auto-completed.
(with TAB-completion being enabled by default in 3.4 ;-))

> the change of print from a statement to a function has also been noted 
> as one of the biggest problems with compatibility of third party user
> scripts for Linux distros changing the default system Python to Python 3

Well, I know 2to3 has had some bad press recently, but it should still make it a piece of cake to convert such small scripts.
(since you don't need to keep 2.x compatibility for them)
msg195682 - (view) Author: Peter Otten (peter.otten) * Date: 2013-08-20 09:27
> a bare expression is not call

Wouldn't that silently swallow a lot of bare

print

statements?
msg195684 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2013-08-20 09:41
> Ambiguous cases (such as "expr * expr" and "expr ** expr") obey the rule that implicit calls are very *low* precedence, so you have to use parens to force the call interpretation.

Does "a + b c" mean "(a + b)(c)"? Does "a + b (c)" mean "(a + b)((c))"?

What does "a (b, c)" mean? "a.__call__(b, c)" or "a.__call__((b, c))"?
msg195690 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2013-08-20 14:06
As Serhiy's examples show, the ambiguities this introduces get confusing fast. A more constrained version that (for example) permitted only name references for the callable could resolve that, but it's probably still a bad idea. Still, interesting to know it is *technically* possible, if anyone decides to pursue it further :)

The "silently ignores bare print statements" aspect isn't new - Python 3 already behaves that way, since a print on its own line now just references the builtin without calling it.
msg195694 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2013-08-20 14:32
I like a parens-less call syntax, but it is unpythonic and even anti-pythonic. It can be good in some other language (like Forth or Tcl, may be Perl or Ruby) but not in Python. It requires some other coordinated features (a syntax to obtaining a reference to a function/method, perhaps shorter syntax for lambdas, braced blocks, different scoping and binding rules) and different philosophy. It doesn't quack as Python.
msg195695 - (view) Author: Ramchandra Apte (Ramchandra Apte) * Date: 2013-08-20 14:42
I do not see much use of of implicit call syntax, a good editor will autocomplete the brackets.
msg195697 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2013-08-20 15:17
Well aren't there other languages (Ruby, Coffeescript) that have the same syntax?  How do they do it?  (Haskell does it too, but I don't think we can learn from it -- but in Ruby and IIRC Coffeescsript it is syntactic sugar for a regular call.)

Heck, even Python's predecessor, ABC, used "f x" to mean "f(x)".  It solved "a b + c" by giving function call binding the *tightest* possible priority though, interpreting it as (a b) + c, and hence (a(b)) + c", which would be no good for Nick's hope that "print x+1, y" can be made to mean print(x+1, y) -- ABC would interpret it as "(print(x)) + 1, y".

Still, I'd like to hear about how Ruby/Coffee solve this.
msg195698 - (view) Author: Alex Gaynor (alex) * (Python committer) Date: 2013-08-20 15:20
I suppose I'm one of the more qualified people to comment on how Ruby does it: a mess of hacks in the lexer/parser. Ruby's case is complicated by the fact that a bare `foo` can either be a local variable or a method call on self. Consider the case `a +b`, should that be parsed as a call to a with a unary + on b, or an addition? In Ruby this depends on whether `a` is already defined as a local. Basically I think this is a terrible idea, and would encourage as strongly as possible to not consider this.
msg195702 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2013-08-20 16:49
To clarify how my patch works: leaving out the parentheses is permitted
*only* when the call is a statement unto itself. That's how it avoids
conflicting with any existing syntax like "a *b" or "a **b": the parser
consumes those as part of the initial expression, so you *need* the parens
to control precedence and get it interpreted as a call instead. It was
surprisingly elegant at the implementation level - once I got the new
expr_stmt definition and parsing right, everything later in the chain "just
worked".

A real patch might want to look at the following enhancements:
* constraining the LHS further. This needs to happen at the AST
construction stage, since the parser can't look far enough ahead to limit
it. The patch currently uses the same rules as augmented assignment targets
* constraining the RHS. For example, disallowing generator expressions, so
they need to use the parenthesised form.
* adding an AST node for clean source-to-source transformations
* allowing implicit calls in more places. For example, as an assignment
value, or a return value.

Proving it was possible at all with pgen scratched my itch, so someone else
will need to run with it for it to turn into a concrete proposal that
addresses the readability issues.
History
Date User Action Args
2013-08-21 01:50:17ethan.furmansetnosy: + ethan.furman
2013-08-20 16:49:15ncoghlansetmessages: + msg195702
2013-08-20 15:20:07alexsetnosy: + alex
messages: + msg195698
2013-08-20 15:17:00gvanrossumsetmessages: + msg195697
2013-08-20 14:42:46Ramchandra Aptesetnosy: + Ramchandra Apte
messages: + msg195695
2013-08-20 14:32:34serhiy.storchakasetmessages: + msg195694
2013-08-20 14:06:16ncoghlansetstatus: open -> closed
resolution: rejected
messages: + msg195690

stage: resolved
2013-08-20 09:41:13serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg195684
2013-08-20 09:27:03peter.ottensetnosy: + peter.otten
messages: + msg195682
2013-08-20 08:25:44pitrousetnosy: + pitrou
messages: + msg195681
2013-08-20 06:56:03ezio.melottisetnosy: + ezio.melotti
2013-08-20 04:59:08ncoghlancreate