For review: PEP 308 - If-then-else expression

Bengt Richter bokr at oz.net
Sun Feb 9 06:55:11 EST 2003


On Fri, 07 Feb 2003 12:14:40 -0500, Guido van Rossum <guido at python.org> wrote:

>Given that if-then-else expressions keep being requested, I hereby put
>forward a proposal.  I am neutral on acceptance of the proposal; I'd
>like the c.l.py community to accept or reject it or come up with a
>counter-proposal.  Please understand that I don't have a lot of time
>to moderate the discussion; I'll just wait for the community to
>discuss the proposal and agree on some way to count votes, then count
>them.
>
>
>PEP: 308
>Title: If-then-else expression
>Version: $Revision: 1.1 $
>Last-Modified: $Date: 2003/02/07 17:03:31 $
>Author: Guido van Rossum
>Status: Active
>Type: Standards Track
>Content-Type: text/plain
>Created: 7-Feb-2003
>Post-History: 7-Feb-2003
>
>
>Introduction
>
>    Requests for an if-then-else ("ternary") expression keep coming up
>    on comp.lang.python.  This PEP contains a concrete proposal of a
>    fairly Pythonic syntax.  This is the community's one chance: if
>    this PEP is approved with a clear majority, it will be implemented
>    in Python 2.4.  If not, the PEP will be augmented with a summary
>    of the reasons for rejection and the subject better not come up
>    again.  While I am the author of this PEP, I am neither in favor
>    nor against this proposal; it is up to the community to decide.
>    If the community can't decide, I'll reject the PEP.
>
>
>Proposal
>
>    The proposed syntax is as follows:
>
>        <expression1> if <condition> else <expression2>
>
>    This is evaluated like this:
>
>    - First, <condition> is evaluated.
>
>    - If <condition> is true, <expression1> is evaluated and is the
>      result of the whole thing.
>
>    - If <condition> is false, <expression2> is evaluated and is the
>      result of the whole thing.
>
>    Note that at most one of <expression1> and <expression2> is
>    evaluated.  This is called a "shortcut expression"; it is similar
>    to the way the second operand of 'and' / 'or' is only evaluated if
>    the first operand is true / false.
>
>    To disambiguate this in the context of other operators, the
>    "if...else" part in the middle acts like a left-associative binary
>    operator with a priority lower than that of "or", and higher than
>    that of "lambda".
>
>    Examples of how this works out:
>
>        x if C else y if D else z <==> x if C else (y if D else z)
>        x or y if C else z        <==> (x or y) if C else z
>	x if C else y or z        <==> x if C else (y or z)
>        lambda: x if C else y     <==> lambda: (x if C else y)
>        x if C else lambda: y     <==> SyntaxError
>        x if C else y, z          <==> (x if C else y), z
>        x, y if C else z          <==> x, (y if C else z)
>
I really dislike this because of the ordering, and the keyword usage suggests
that what you are reading is statements, not expressions.

The purpose is to evaluate one expression and use the result to select one of
some other set of expressions to evaluate for a value of the whole expression.

Why not a simple enumeration of the candidates and a choice based on the
integer value of the condition? E.g., with a single "?" implying bool(cond) and
"??" implying int(cond),

    cond ? false_sel, true_sel  # with optional parens
    bool(cond) ?? false_sel, true_sel  # equivalent to above, and
    event_no ??  evt_zero(), evt_one(), evt_two() # , ... selects expr to eval based on integer >=0

>        x if C else y if D else z <==> x if C else (y if D else z)
         C ? (D? z, y), x
>        x or y if C else z        <==> (x or y) if C else z
         C ? z, x or y

>	x if C else y or z        <==> x if C else (y or z)
        C ? y or z, x

>        lambda: x if C else y     <==> lambda: (x if C else y)
         C ? y, lambda: x

>        x if C else lambda: y     <==> SyntaxError
         C ? lambda: (y, x)        # C==False => lambda: (y,x)  C==True => IndexError exception
         C ? (lambda: y, x)        # C==False => lambda: (y,x)  C==True => IndexError exception
         C ? (lambda: y), x        # C==False => lanbda: y      C==True => x


>        x if C else y, z          <==> (x if C else y), z
         C ? y, x, z              

>        x, y if C else z          <==> x, (y if C else z)
         x, C ? z, y
>
You just have to remember that you are selecting the expression based on bool(C),
so the order is False, True


>

>Alternatives
>
>    Many C-derived languages use this syntax:
>
>        <condition> ? <expression1> : <expression2>
>
>    I reject this for several reasons: the colon already has many uses
>    in Python (even though it would actually not be ambiguous, because
>    the question mark requires a matching colon); for people not used
>    to C-derived language, it is hard to understand.
>
>
>    Eric Raymond proposed a variant that doesn't have this problem:
>
>        <condition> ? <expression1> ! <expression2>
IMO
         <condition> ? <expression_F> , <expression_T>

is pretty natural -- it reads ltr, and selects the expression to evaluate
like indexing the tuple of candidates with bool(<condition>). It also extends
naturally to integer-indexed selection for selection from more expressions

         <condition> ?? <expression0> , <expression1>, <expression2>, ...
  
>
>    While cute, this suffers from the Perlish problem of using
>    arbitrary punctuation with an arbitrary meaning; and it's no
>    easier to understand than the ?: form.
>
>
>    If we could live with adding a new keyword, we could use:
>
>        if <condition> then <expression1> else <expression2>
IMO this clutters possible nested use with confusing keywords instead of
having simple expression syntax.

>
>    Apart from the problem of introducing a new keyword for a minor
>    feature, this also suffers from ambiguity at the start of a
>    statement; for example:
>
>        if verbose then sys.stdout.write("hello\n") else None
>
>    could be an syntactically correct expression statement, but starts
>    with 'if', which makes the parser believe it is the start of an
>    'if' statement.  To resolve this, the syntax would have to require
>    parentheses, which makes it uglier.  However, this form has the
>    advantage of evaluating strictly from left to right (not that that
>    is a requirement for being Pythonic -- list comprehensions don't).
>
         verbose ? None, sys.stdout.write("hello\n")

would seem to take care of that. Using ?? to mean select by integer index
from more than two expressions to evaluate, would also allow, e.g.,

        result = i>=0 ? "low", i<=3 ? "high",  i ?? "zero", "one", "two"  # <==>
        result = (i>=0 ?
                     "low", 
                     (i<=3 ?
                         "high",
                         (i ?? "zero", "one", "two")))

Or perhaps,

        status = event_no ?? action_0(), action_1("some arg"), action_2()

etc., etc.

Alternatively, the condition could also be the first listed inside mandatory
specially decorated (with ? or ??) parentheses, e.g.,

        (? verbose, None, sys.stdout.write("hello\n"))
        status = (?? event_no, action_0(), action_1("some arg"), action_2())
 
Regards,
Bengt Richter




More information about the Python-list mailing list