(partial) summary of ideas for the ternary operator

Michele Simionato mis6 at pitt.edu
Sat Feb 8 16:50:08 EST 2003


Having read the thread on the ternary operator, I would like to comment
some of the ideas that have been proposed and asking for new ones ;-)

I am one of the persons who misses the ternary operator (even if I do *not*
come from C/C++). I would often use it in functional style programming and/or
in list comprehensions.  Also, I find it useful when I have to plot functions.
For instance, I am not satisfied with the current way of writing, let say, 
a safe square root in the following form,

def safesqrt(x)
   if x>0:
      s=math.sqrt(x)
   else:
      s=0
   return s

to be used in a list comprehension as

sqrt_ls=[safesqrt(x) for x in range(-5,5)]

In this example, I was forced to give a name to a not worthwhile function 
safesqrt, and to write 6 lines of code plus a blank line (okay, I could 
have skipped two lines as in

def safesqrt(s)
   if x>0:
      return math.sqrt(x)
   return 0

but this would have been less readable in my view), for no really
good reason. Moreover, when another programmer looks at the list comprehension, 
she is is forced to look up at safesqrt definition, in order to understand 
what the list comprehension is doing. (all these points, I think have been
by others too, but it is worth repeating).

I find much more elegant, less error prone and more readable 
(IMHO) the one-liner

sqrt_ls=[math.sqrt(x) if x>0 else 0 for x in range(-5,5)]

Unfortunately, as pointed out by many, the "if" keyword is already overloaded
and can easily cause confusions. The (perhaps) most horrible abuse has been
shown by Holger Krekel:

if obj() if iscallable(obj) else default:

But even in my simple example one could easily multiply the if's:

only_even=True
sqrt_ls=[math.sqrt(x) if x>0 else 0 for x in range(-5,5) if x %2 ==0] \
        if only_even else [math.sqrt(x) if x>0 else 0 for x in range(-5,5)]

It is clear that the ternary operator in the "if" form can be horribly
abused. This is the reason why I would prefer the introduction of a new
keyword "when" (proposed by Sean Ross). For instance:

  sqrt_ls=[when x>0 math.sqrt(x) else 0 for x in range(-5,5)]

"when" can also be abused, but still it is more readable than "if":

  if (when iscallable(obj) obj() else default):

This is ugly, but still less confusing than the "if". Of course, a real 
programmer would *not* use such constructs, therefore this example is 
quite academic. With the "when" keyword my list comprehension example 
would be ugly but still comprehensible:

only_even=True
sqrt_ls=when only_even [when math.sqrt(x) else 0 for x in range(-5,5) 
                       if x %2 ==0] \
        else [when x>0 math.sqrt(x) else 0 for x in range(-5,5)]

Notice that having "when" before the conditional expression enhances the
readability, but even

  s=sqrt(x) when x>0 else 0

is not so bad. This form would be nicer if most of the times the condition 
was true and the else clause was referring to a rather exceptional case. Still,
this second form would be more abuse-friendly as in

  if sqrt(x) when x>0 else 0 > 2: #abominable

meaning (maybe)

  if (sqrt(x) when x>0 else 0) >2: #still very confusing

Putting "when" at the beginning improve the situation:

  if (when x>0 sqrt(x) else 0)>2: #no good style, but less confusing

The main disadvantage of "when" is that it introduces a new keyword,
potentially breaking old code. However, to call an identifier "when"
is rather unlikely. I am pretty sure I never used an identifier called
"when" in my code. However, I have scanned the entire Python-2.2 library in 
/usr/local/lib/python2.2/ (597 '.py' files, total of 66492 lines of code) 
and I have found that actually "when" *is* used as an identifier.
This happens only once, in the tty module, and there is a trivial fixing
to that (s/when/when_/)

Still, people could be inconfortable with a new keyword (I personally 
I am willing to pay that price to have a *readable* ternary operator).
Alternatively one could try to overload the meaning of while, as in the 
following:

sqrt_ls=[math.sqrt(x) while x>0 else 0 for x in range(-5,5) if x%2==0]

however I am unhappy with this solution because one could abuse it
in the loops:

while obj() while iscallable(obj) else default:

Of course, one could require mandatory parenthesis

while (obj() while iscallable(obj) else default):

but I don't think this is a real solution.

A better way to avoid the introduction of a new keyword is
the introduction of a new punctuation:

  sqrt_ls=[x>0 ? math.sqrt(x) else 0 for x in range(-5,5)]

This is less pythonic than the "when" solution, but would make
happier C/C++/Perl/etc.etc. programmers. It is also quite readable,
I think.

while (iscallable(obj) ? obj() else default):
if (iscallable(obj) ? obj() else default):

becomes now understandable, if not easy to read. This is backward
compatible, but I am not sure if the introduction of a new punctuation
makes happy old pythonistas.

In summary, I would say the following:

 1. After the introduction of functional programming constructs (lambda, 
    map, etc), of list comprehension and of a boolean type in Python,
    now there is more need for a ternary operator than ever.
    I think Guido himself recognizes that since for the first time now
    he is giving his disponibility to add a ternary operator.

2. If we want a Pythonic (i.e. readable) ternary operator that even
   newbies can understand, we have to pay a price: add a new kewyword
   or new punctuation.

3. If we stay with "if", abuses become easier. I think the original examples
   of Guido were intended to point out these shortcomings. They improve a
   little with the "when"/"?" notations:

when C x else when D y else z   <=>  when C x else (when D y else z) 
when C x or y else z         <=>  when C (x or y) else z 
when C x else y or z         <=>  when C x else (y or z)  
lambda : when C x else y     <=>  lambda : (when C x else y)  
when C x else lambda : y     <=>  SyntaxError
when C x else y,z            <=>  (when C x else y),z     
x, when C y else z           <=>  x, (when C y else z)    

C ? x else D ? y else z   <=>  C ? x else (D ? y else z) 
C ? x or y else z         <=>  C ? (x or y) else z 
C ? x else y or z         <=>  C ? x else (y or z)  
lambda : C ? x else y     <=>  lambda : (C ? x else y)  
C ? x else lambda : y     <=>  SyntaxError
C ? x else y,z            <=>  (C ? x else y),z     
x, C ? y else z           <=>  x, (C ? y else z)    

4. I am willing to concede that in any proposal the ternary operator can be 
   abused, and that it is less obvious than an if statement. 
   Nevertheless, it is not a case that so many languages have a ternary 
   operator: it is useful, and *can* be used without abusing it, if the 
   programmer is a little of reasonable. Moreover, any of these proposals 
   is better than the currently employed horrible hacks with and/or and 
   [True,False][not condition]


I am +0.5 for Guido's proposal, +1 for a proposal involving "when" or "?".
Maybe "?" is better, since does not break old code never and it is
clearer for C programmers (even for me, and I am not a C programmer ;);
however it is less pythonic. 

I am very curious of seeing the result of the final votation on the
ternary operator. Happy discussion,


                                         Michele

P.S. the proposal by Holger Krekel's is not so bad,

  s=sqrt(x) except x<0: 0

since it has the advantage of avoiding the introduction of a new keyword and
makes obvious which is the expected case. However, I don't like the abuse of
colons, I would prefer the syntax

  s=sqrt(x) except x<0 then 0

or something similar, but still it would be unclear on "if" statements:

  if (sqrt(x) except x<0 then 0)>2: #disaster

Waiting for other ideas ...




More information about the Python-list mailing list