PEP 308: an additional "select/case" survey option

Clark C. Evans cce at clarkevans.com
Wed Mar 5 03:47:53 CET 2003


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  switch-case
   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 reduce data 
   duplication (both the assignments and conditional tests)
   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
              case 0: 'no exit'
              case 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)
                 case < .0001: 0
                 case > .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')
              case true: s.readlines()
              else: s.split()
 
     z = 1.0 + select abs(z)
                case < .0001: 0
                else: z

     t = v[index] = select t
                      case <= 0:  t - 1.0
                      else: -sigma / (t + 1.0)

     return select len(s)
              case < 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
               case 1: -2
               case 2: -1
               case between 3,4: 0
               case 5: +1
               case 6: +2
               else: raise "invalid die roll %d " % roll

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.  

  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