[Python-3000] It's a statement! It's a function! It's BOTH! (original) (raw)

Talin talin at acm.org
Sun Apr 2 02:52:11 CEST 2006


This is about the print / writeln debate.

Let me say up front that I don't expect this posting in its original form to be widely accepted (In fact, I'll be disappointed if I don't get at least a sum total of -5000 on responses to this.) At the same time, however, I think that the issue that I am raising is legit, even if my solution is not.

Part of my issue is that I like both solutions. That is, the "professional programmer" part of me likes the stream.writeln, as seen in many other languages, in particular Java and C#. On the other hand, the "recreational doodler" part of me remembers with fondness the simplicity and ease of learning of my first experiences in BASIC, where it was completely intuitive and natural to just say "print x" and not have to understand about the complexities of line endings, function argument lists, and so on. And my notion of "pythonic" includes the eschewing of needless delimiters and other non-word characters when possible.

For the most part, the print statement isn't that much different from a function, except that it doesn't have parentheses. (I'll discuss the exceptions to this later.) That is, it takes an argument list which is separated by commas, and each of those arguments is treated pretty much the same.

However, Python doesn't recognize the general concept of a paren-less function call, so that's why print has to be a special case, that's built into the parser.

I've read a number of posts which all sort of lead up to the idea - why can't we allow the interpreter to recognized paren-less functions? Now obviously we wouldn't want every function to act this way, as it would lead to a nightmare of ambiguities. So you'd have to have some means of telling the parsers which functions were to be parsed as "statements" and which should not.

The difficult with that notion is that now you are asking the Python parser to do something that it never did before, which is to make parsing decisions based on semantics rather than just syntax. While there's no technical difficulty with this, it goes against the Python tradition in a fairly fundamental way.

But what the heck, lets forget tradition for a moment and see what would happen if we were to go ahead and do it anyway.

We would start by defining a simple metalanguage for giving instructions to the parser. I won't even try to suggest a syntax, but in essence this would be something that has the same role as a macro language or preprocessor, in that it is not part of the programming language itself, but instead describes how the subsequent text is to be interpreted. (There's already some precedent for this with the future syntax.)

Specifically, one of the commands of this metalanguage would be a command to parse an expression beginning with a specific keyword (such as "print") as a statement rather than as an expression. A comma-separated list of arguments would follow the initial identifier, and these would be treated as result function arguments.

Thus you could have:

print a, b, c send a, b, c read a, b, c

...and so on. Yes, its true - there is vast potential for abuse here, I don't deny it. (With great power...etc.)

Now, about those exceptions:

One that's fairly easy to handle is the "print >> stream" syntax. We can tweak the syntax for the "function as statement" so that instead of the rule being:

identifier [ arg, ... ]

it can be:

expression [ arg, ... ]

Thus, the parser sees the word "print" which tells it that we're going to have a paren-less function, but it still parses the text "print >> stream" as an expression. Then its simply a matter of overloading the ">>" operator to return a closure function that prints to the given stream. The semantics are equivalent to:

(print >> stream)( arg, arg, arg )

OK, so suppose you find this just too wide open for abuse. An alternative is to dump the ">>" syntax and use a keyword argument:

print stream=fh, arg, arg, arg

Since print is being executed as a normal function call, except without parens, we would expect the normal keyword argument syntax to work, as well as *args and **args. (Note that in this case the keyword argument is coming before the non-keyword arguments, which is something that I hope will be addressed in Python 3000.)

Now, what about the semantics of the "spaces between args"? There's a couple of ideas:

  1. Define "print" as putting spaces between args, and "write" as not doing so. 2) Have a keyword arg that allows specifying a separator char, where space is the default:

No space between args

print sep="", arg, arg, arg

Heck, why not even make it a function:

Insert enough spaces between args to align to 8-char boundaries

print sep=tabToNext( 8 ), arg, arg, arg

Finally, there is the issue of the trailing comma to suppress the final newline. I must confess that I don't have a clever solution in this case (I can think of lots of hacky solutions...) I suspect that the best compromise is to have distinct "print" and "println" functions to cover this case.

-- Talin



More information about the Python-3000 mailing list