[Python-ideas] PEP 531: Existence checking operators

Nick Coghlan ncoghlan at gmail.com
Sat Oct 29 02:21:18 EDT 2016

On 29 October 2016 at 14:52, Nick Coghlan <ncoghlan at gmail.com> wrote:
> And that reflects the problem Paul and David highlighted: in any
> *specific* context, there's typically either only one sentinel we want
> to either propagate or else replace with a calculated value, or else
> we want to handle different sentinel values differently, which makes
> the entire concept of a unifying duck-typing protocol pragmatically
> dubious, and hence calls into question the idea of introducing new
> syntax for working with it.
> On the other hand, if you try to do this as an "only None is special"
> kind of syntax, then any of the *other* missing data sentinels (of
> which we have 4 in the builtins alone, and 5 when you add the decimal
> module) end up being on much the same level as "arbitrary sentinel
> objects" in the draft PEP 531, which I would consider to be an
> incredibly poor outcome for a change as invasive as adding new syntax:
> https://www.python.org/dev/peps/pep-0531/#arbitrary-sentinel-objects

Considering this question of "Am I attempting to extract the right
underlying design pattern?" further puts me in mind of Greg Ewing's
old rejected proposal to permit overloading the "and" and "or"
operators: https://www.python.org/dev/peps/pep-0335/

After all, the proposed "?then" and "?else" operators in PEP 531 are
really just customised variants of "and" and "or" that use a slightly
modified definition of truth-checking. PEP 335 attempted to tackle
that operator overloading problem directly, but now I'm wondering if
it may be more fruitful to instead consider the problem in terms of
the expanded conditional expressions:

* "LHS and RHS" -> "RHS if LHS else LHS"
* "LHS or RHS" -> "LHS if LHS else RHS"

A short-circuiting if-else protocol for arbitrary "THEN if COND else
ELSE" expressions could then look like this:

    _condition = COND
    if _condition:
        _then = THEN
        if hasattr(_condition, "__then__"):
            return _condition.__then__(_then)
        return _then
        _else = ELSE
        if hasattr(_condition, "__else__"):
            return _condition.__else__(_else)
        return _else

"and" and "or" would then be simplified versions of that, where the
condition expression was re-used as either the "THEN" subexpression
("or") or the "ELSE" subexpression ("and").

The reason I think this is potentially interesting in the context of
PEPs 505 and 531 is that with that protocol defined, the
null-coalescing "operator" wouldn't need to be a new operator, it
could just be a new builtin that defined the appropriate underlying
control flow:

    value = if_set(expr1) or if_set(expr2) or expr3

where if_set was defined as:

    class if_set:
        def __init__(self, value):
            self.value = value
        def __bool__(self):
            return self is not None
        def __then__(self, result):
            if result is self:
                return self.value
            return result
        def __else__(self, result):
            if result is self:
                return self.value
            return result

Checking for a custom sentinel value instead of ``None`` would then be
as straightforward as using a different conditional control flow
manager that replaced the ``__bool__`` check against ``None`` with a
check against the specific sentinel of interest.


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia

More information about the Python-ideas mailing list