PEP 308: an additional "select/when" survey option
Clark C. Evans
cce at clarkevans.com
Tue Mar 4 23:42:42 EST 2003
After a side discussion, it seems that this construct is being
confused with C's switch/case. It is quite different than switch/case
since it is an expression (has a value) rather than a block construct
containing statements. It's actually more like SQL's CASE/WHEN,
which, btw, has two forms:
CASE WHEN value = 0 THEN 'zero'
WHEN value = 2 THEN 'one'
ELSE 'unknown'
END
and...
CASE value
WHEN 0 THEN 'zero'
WHEN 2 THEN 'one'
ELSE 'UNKNOWN'
END
So, I was thinking about three modifications to the proposal,
1. Change 'case' to 'when' so that confusion with
switch/case is harder to make.
2. Allow the 'select' keyword to be missing so that
the 'when' items can be plain old boolean expressions
like the 1st case above.
3. Make it clear in the motivation that this is an
expression (with a value) not a block structure;
as a result, this greatly improves the power of
lambda functions which as I understand can't
contain statements, and thus can't do if/then
sorts of logic.
The updated proposal/announcement...
-----
After some encouragement by Raymond, I'd like to add one more
item to the survey, if you like what follows perhaps you can
even *change* your vote (Raymond?) to include:
Q accept select/when
Z deny everything else!
Dedication:
To those who hate the terinary operator beacuse it isn't pythonic...
Background:
After looking over much of my Python source code, I found that
where I thought I needed a terinary operator, I ended
up taking one of two paths:
if 0 == quantity: exit = 'no exit'
elif 1 == quantity: exit = 'a door'
else: exit = '%s doors' % quantity
Or, the more concise mapping-idiom:
exit = { 0: 'no exit',
1: 'a door' }.get(quantity,
'%s doors' % quantity)
The latter construct has two advantages over the if/else
statement level solution:
1. It's clear that I'm making an assignment to exit;
in effect the "exit =" isn't duplicated and this
aids in authoring/maintenance/reading.
2. The ugly '== quantity' isn't duplicated for each line,
once again improving maintenance.
However, the mapping-idiom has three problems:
1. The conditional switch isn't exactly "obvious" here
unless you're a Python vetran, this hurts in the
maintenance arena; besides the 'else' case is ugly.
2. It doesn't short-circut cleanly, to do a short-circut
you need to use lambda's... ick; further, it results in
the construction of a mapping which may not really help
out optimizations.
3. It really doesn't facilitate the use whitespace/indentation
to re-inforce a visual representation of the program's
structure.
Philosophy:
The whole rationale for this construct is to introduce
a conditional 'expression' construct, and further to provide
this construct in such a way as to reduce duplication (of both
assignment and conditional fragments) to increase maintenance.
The goal is not to save on vertical screen realistate by
enabling multi-line constructs to be "jumbled" into a
single line. It seems that many people are asking for the
terinary option for the latter.
Instead, this proposal seeks to engage Python's unique approach
to syntax by using whitespace to enhance the visual representation
of the program's structure. Many Python converts are here exactly
beacuse Python is very readable and thus maintainable. This proposal
is here soley to re-enforce this "pythonic" approach to coding.
Proposal:
The proposal introduces a 'select' or 'switch' keyword which creates
an indented expression block. Instead of the following,
exit = { 0: 'no exit',
1: 'a door' }.get(quantity,
'%s doors' % quantity)
You could write,
exit = select quantity
when 0: 'no exit'
when 1: 'a door'
else: '%s doors' % quantity
This proposal gives you the power of the mapping-idiom without
the uglyness. It expresses the intent of the construct in a
very human readable manner using whitespace smartly.
While the above is "good", it assumes an equality operator. So
that the structure is more generic, his proposal allows an optional
operator (a function taking 2 or more value and returning a boolean)
immediately following the 'case' label,
z = 1.0 + select abs(z)
when < .0001: 0
when > .5: .5
else: -sigma / (t + 1.0)
Note that the examples given in the proposal are thus
very easily expressed using this notion:
data = select hasattr(s,'open')
when true: s.readlines()
else: s.split()
z = 1.0 + select abs(z)
when < .0001: 0
else: z
t = v[index] = select t
when <= 0: t - 1.0
else: -sigma / (t + 1.0)
return select len(s)
when < 10: linsort(s)
else: qsort(s)
The 'operator' need not be binary, one could, for example,
provide a terinary operator, such as:
def between(cmp,rhs,lhs): return cmp >= rhs and cmp <= lhs
score = select die
when 1: -2
when 2: -1
when between 3,4: 0
when 5: +1
when 6: +2
else: raise "invalid die roll %d " % roll
As a further (quite optional) enhancement, allow 'select'
keyword to be absent, and in this case, the 'when' statement
is evaulated as a boolean expression.
exit = when 0 == quantity: 'no exit'
when 1 == quantity: 'a door'
else: '%s doors' % quantity
Summary:
The proposal thus creates a flexible mechanism for avoiding
the excessive duplication of assignment and equality fragments
within a conditional assignment nest; it does so through a
new expression structure which, like the rest of Python, uses
indentation for structure.
This proposal thus provides the pratical benefit of a terinary
operator, while at the same time opening the door to a rich
(and quite readable) conditional assignment mechanism. As a
side effect, it could make lambda functions more powerful.
In particular, this proposal rejects the rationale for 'terinary'
operator as a way to trade horizontal screen realestate for
vertical space; as this rationale is in direct opposition to
the fundamental principle of readability. And as such, the
proposal explicitly does not include a way to include multiple
case labels on the same line.
Credits:
Alex Martelli for validating the elimination of needless-duplication
of code as the primary goal for the construct; and
Carel Fellinger for presenting the idea of a plugable predicate/operator
Raymond Hettinger for asking me to write this up more or less formally
More information about the Python-list
mailing list