Proposed PEP for a Conditional Expression
Michael Chermside
mcherm at destiny.com
Sun Sep 9 10:42:49 EDT 2001
Included in this email is a PEP which I am putting together dealing with
the idea of introducing a conditional expression into Python. I would
welcome any discussion, suggestions, and/or help, sent either to this
newsgroup under this topic, or emailed to me at <python at mcherm.com>.
For the sake of full disclosure, I'll also express my own opinion here
(I have tried hard to keep the PEP evenhanded). With reference to the
PEP, I find ARG_1, ARG_3, and ARG_23 unconvincing, while ARG_21, ARG_22,
and ARG_3 all make sense to me. But for me, ARG_24 is the overwhelming
point and convinces me that adding a conditional expression would be a
good idea. I prefer the syntax of SPEC_2, but am certainly open to other
ideas.
I encourage others to let me know (in a form similar to the above) what
they find convincing or not, and please suggest new arguments that I've
missed.
------------------- DRAFT PEP FOLLOWS ----------------------------------
PEP: NO-NUM-YET
Title: Creating a Short-Circuiting Conditional Expression
Version: $Revision: $
Last-Modified: $Date: $
Author: python at mcherm.com (Michael Chermside)
Status: Draft
Type: Standards Track
Python-Version: 2.5
Created: NOT-CREATED-YET: dd-mmm-yyyy
Post-History:
Abstract
A Frequently Asked Question by Python newbies is "How do I do the
equivalent of C's ?: syntax?". The answers, while fairly well
documented, are imperfect or incomplete. Thus many have suggested
adding some syntax to Python for this purpose. The goal of this
PEP is to gather all of the commonly proposed options and the
arguments both pro and con. It will thus serve as a focal point
for discussion and perhaps even be eventually accepted or
rejected.
Rationale
There are quite a few arguments and rationalizations for the
various proposals. Here we will distinguish between arguments for
one syntactic form over another (which should appear in the
Specification section), and arguments for or against having a
conditional expression at all will appear here.
XXX - After further discussion, either a community consensus will
be reached, or the BDFL will be invited to make a pronouncement,
at which point that conclusion will be be given here. All of the
arguments will be retained, and comments will be added here
saying which arguments were the most influential.
ARG_1: Workarounds exist.
Here are a few workarounds. See also the Python FAQ [1], and
the cookbook [2]. First, the things that DON'T work:
WORKAROUND_1: (this fails)
> def cond(c, a, b):
> if c: return a
> else: return b
> x = cond(c, a, b)
This fails, because it does not short-circuit... the
arguments the the function are always evaluated before
the function is invoked. Thus, for instance, the
following would not work properly when a == 0:
> x = cond( a==0, DEFAULT_VAL, 100/a )
WORKAROUND_2: (this SOMETIMES works)
> x = c and a or b
Because the boolean operators short circuit, this idiom
actually works quite nicely some of the time. As long as
you are sure that a will evaluate as true, this will do
the trick. It's also more readable than the other working
solutions, although it is still quite difficult to scan
for people not familiar with the idiom. If, however, a
evaluates as false, then this fails.
WORKAROUND_3: (this is the most common idiom)
> x = (c and [a] or [b])[0]
Here we create one-element lists and index into them.
Since [a] is definitely NOT false (it's got 1 element),
the and-or syntax will succeed.
WORKAROUND_4: (lambda also works)
> x = (c and (lambda:a) or (lambda:b))()
This is equivalent to WORKAROUND_3, but most people
consider it to be slightly less readable, particularly
since conditional statements are often used in lambda
expressions, thus complicating things further.
WORKAROUND_5: (just write it out!)
> if c:
> x = a
> else:
> x = b
Of course, many people would say that just writing it out
is probably the best solution. It can be done in 4 line,
or in 2, like this:
> if c: x = a
> else: x = b
In either case, though, this is NOT an expression, so
using it may require creating a temporary variable and
splitting a formula into two lines, or replacing a lambda
expression with a named function.
ARG_2: No need to clutter language.
BECAUSE workarounds exist, it is really not necessary to
clutter the language with additional ways to express the same
thing. It is a good thing for a language to be simple, and
Python has this, although if it keeps adding new features all
the time, it will eventually lose it.
ARG_3: Not a readable syntax.
The existance of a conditional expression syntax tends to
lead programmers astray. It encourages unreadable syntax like
this:
x = c1 ? c2 ? a : b : c3 ? c4 ? d : e : f
The use of syntax like if-then-else which scans more easily
will not help with the fundamental problem here, which is
that structurally, conditional expressions are easily abused
to create unreadable code. The above would be better
expressed on multiple lines, like this:
if c1:
if c2: x = a
else: x = b
else:
if c3:
if c4: x = d
else: x = e
else: x = f
In order to encourage Python programmers to write readable
programs, we should not allow conditional expressions.
ARG_21: Simplifies lambda expressions.
Lambda expressions allow one to create an annonomous function
on the fly and, while some rail agains them, others find them
both useful and expressive. However, they do NOT allow
statements, only an expression. Thus, for instance, variables
cannot be rebound in a lambda expression, since this can only
be achieved with a statement. This is generally considered to
be a good thing. However, since conditionals cannot (without
this PEP) be easily utilized in an expression, they too are
mostly unavailable in a lambda, and this restriction is one
most lambda users would like to see lifted. Here is an
example of the sort of code that many lambda users would like
to be able to write:
inverses = map( lambda x: x==0 ? "Inf" : 1.0/x, values )
ARG_22: Makes a functional style easier.
One nice feature of the Python language is that it allows the
use of a number of different programming paradigms ranging
from simple scripting to object oriented systems. One in
particular is functional programming (or at least programming
which uses many of the same approaches as would be used in a
true functional language). Features like list comprehensions
make this paradigm particularly well supported in Python.
However, there are many places where a functional style would
be much easier if a conditional expression were available.
Here is a simple example:
tags = [ s is None ? None : '<%s>' % s for s in keys ]
ARG_23: Lots of people ask for it.
Lots of people ask for a conditional expression. Even if the
other pro arguments don't convince you, what harm is there in
settling this one? For a LONG time, we used to hear
complaints about how Python lacked "+=", "*=" and such; now
that it has them, the complaints have died to a trickle.
What's wrong with just satisfying people?
ARG_24: Clarifies intent when used for assignments.
Compare the following bits of code:
Sample 1:
x = c ? a : b
Sample 2:
if c:
x = a
else:
x = b
Sample 3:
if c:
x = a
else:
y = b
Notice how using the conditional expression in sample 1 meant
that we didn't need to write "x" twice? Now, assignment
targets are rarely complex, so we're not saving many
keystrokes by typing "x" only once, but we ARE clarifying the
intent. Sample 2 makes it appear as if we want to do
DIFFERENT THINGS depending on c, when in fact, we always want
to do the SAME thing (set x), just with different values.
Sample 3 looks quite similar to Sample 2, but the
programmer's intent is completely different.
Specification
In order to try to capture the numerous suggestions that have
been made, this section will list SEVERAL specifications, each
with an identifying name (SPEC_x). XXX - After further
discussion, one specification should be selected as "preferred".
This one should be listed first, and identified in this
paragraph.
Arguments for or against HAVING a conditional expression are
found in the Rationale section, the arguments here simply deal
with different means of
SPEC_1: Use the "C syntax".
The following production would be added to Python syntax:
expression: expression ? exprression : expression
Since "?" is not currently used in Python, this would never
be syntactically ambiguous. Sample: a = b ? c : d
PRO - [familiar to programmers]
CON - [hard to read] [not pythonic]
SPEC_2: Use "if-then-else".
The following production would be added to Python syntax:
expression: "if" expression "then" expression "else"
expression
The word "then" would become a new keyword, introduced
gracefully through the use of a __future__ statement. I am
currently unclear as to whether this syntax could ever be
ambiguous.
PRO - [readable]
CON - [new keyword]
SPEC_3: Use "if-then-else" without keyword.
The same syntax would apply as in SPEC_2, except that "then"
would not become a keyword. Instead, a special rule would
apply (much like the "as" from import) making it a
pseudo-keyword only after "if" and before the "then"
pseudo-keyword or expression ":" (which would indicate that
it was an if *statement*, not a conditional expression).
PRO - [no new keyword]
CON - [pseudo-keywords are confusing]
SPEC_4: Use "if c: a else: b".
The following production would be added to Python syntax:
expression: "if" expression ":" expression "else" ":"
expression
The famous "dangling-else" problem would NOT arise because
the "else:" part would be mandatory.
PRO - [might allow "if c1: a elif c2: b else: c"]
CON - [elif thing would re-introduce dangling else problem]
SPEC_5: Use "if(c)a:b" syntax.
The syntax "if (condition) trueval : falseval" would be used.
PRO -
CON - [syntax conflict] [too many uses for () already]
[looks like line noise]
Current syntax allows the following:
> b = 'val'
> c = 1
> if (c): b
Precedence:
Orthogonal to the syntax selection is the question of
precedence. Whichever syntax is selected, this PEP proposes
that the precedence of the conditional expression be greater
than that of lambda, and less than that of "or" (see the
precedence chart in the Reference Manual [2]. Thus, the
following:
x = a or b ? c or d : e or f
would be parsed as
x = (a or b) ? (c or d) : (e or f)
rather than
x = a or (b ? (c or d) : e) or f
or anything else, and likewise, that:
x = lambda: a ? b : c
would be parsed as
x = lambda: (a ? b : c)
rather than
x = (lambda: a) ? b : c
Short Circuiting:
Regardless of which syntax selection is made, the evaluation
of the conditional expression will be short circuiting. That
means that in the expression
x = a ? b : c
if a is true, then c will NOT be evaluated, and if a is false
then b will NOT be evaluated. This could be defended on
grounds that it is similar to the short circuiting booleans
in Python, but the REAL reason is much stronger: without
short-circuiting evaluation, most of the utility of the
conditional expression is lost!
Reference Implementation
No reference implementation has been completed as yet. In fact,
no reference implementation has been begun! The implementation is
not expected to be difficult at all, but the design decisions ARE
hard, so there's little sense starting on an implementation until
the PEP status is "Accepted". XXX - After the PEP is accepted (if
it is!) please replace this paragraph with a "help wanted!" sign
<0.1 wink>!
References
[1] Python FAQ: Is there an equivalent of C's "?:" operator?
http://www.python.org/doc/FAQ.html#4.16
[2] Python Reference Manual: Precedence chart
http://www.python.org/doc/current/ref/summary.html
[3] Python Cookbook: "conditionals" in expressions
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52310
Copyright
This document has been placed in the public domain.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End:
More information about the Python-list
mailing list