<div dir="ltr">I certainly like the concept, but I worry that use of <span style="font-size:12.8px"> __exists__() could generalize it a bit beyond what you're intending in practice. It seems like this should only check if an object exists, and that adding the magic method would only lead to confusion.</span></div><div class="gmail_extra"><br clear="all"><div><div class="gmail_signature" data-smartmail="gmail_signature">-Ryan Birmingham<br></div></div>
<br><div class="gmail_quote">On 28 October 2016 at 04:30, Nick Coghlan <span dir="ltr"><<a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi folks,<br>
<br>
After the recent discussions of PEP 505's null-coalescing operator<br>
(and the significant confusion around why anyone would ever want a<br>
feature like that), I was inspired to put together a competing<br>
proposal that focuses more on defining a new "existence checking"<br>
protocol that generalises the current practicises of:<br>
<br>
* obj is not None (many different use cases)<br>
* obj is not Ellipsis (in multi-dimensional slicing)<br>
* obj is not NotImplemented (in operand coercion)<br>
* math.isnan(value)<br>
* cmath.isnan(value)<br>
* decimal.getcontext().is_nan(<wbr>value)<br>
<br>
Given that protocol as a basis, it then proceeds to define "?then" and<br>
"?else" as existence checking counterparts to the truth-checking "and"<br>
and "or", as well as "?.", "?[]" and "?=" as abbreviations for<br>
particular idiomatic uses of "?then" and "?else".<br>
<br>
I think this approach frames the discussion in a more productive way,<br>
as it gives us a series of questions to consider in order where a<br>
collective answer of "No" at any point would be enough to kill this<br>
particular proposal (or parts of it), but precisely *where* we say<br>
"No" will determine which future alternatives might be worth<br>
considering:<br>
<br>
1. Do we collectively agree that "existence checking" is a useful<br>
general concept that exists in software development and is distinct<br>
from the concept of "truth checking"?<br>
2. Do we collectively agree that the Python ecosystem would benefit<br>
from an existence checking protocol that permits generalisation of<br>
algorithms (especially short circuiting ones) across different "data<br>
missing" indicators, including those defined in the language<br>
definition, the standard library, and custom user code?<br>
3. Do we collectively agree that it would be easier to use such a<br>
protocol effectively if existence-checking equivalents to the<br>
truth-checking "and" and "or" control flow operators were available?<br>
<br>
Only if we have at least some level of consensus on the above<br>
questions regarding whether or not this is a conceptual modeling<br>
problem worth addressing at the language level does it then make sense<br>
to move on to the more detailed questions regarding the specific<br>
proposed *solution* to the problem in the PEP:<br>
<br>
4. Do we collectively agree that "?then" and "?else" would be<br>
reasonable spellings for such operators?<br>
5a. Do we collectively agree that "access this attribute only if the<br>
object exists" would be a particularly common use case for such<br>
operators?<br>
5b. Do we collectively agree that "access this subscript only if the<br>
object exists" would be a particularly common use case for such<br>
operators?<br>
5c. Do we collectively agree that "bind this value to this target only<br>
if the value currently bound to the target nominally doesn't exist"<br>
would be a particularly common use case for such operators?<br>
6a. Do we collectively agree that 'obj?.attr' would be a reasonable<br>
spelling for "access this attribute only if the object exists"?<br>
6b. Do we collectively agree that 'obj?[expr]' would be a reasonable<br>
spelling for "access this subscript only if the object exists"?<br>
6c. Do we collectively agree that 'target ?= expr' would be a<br>
reasonable spelling for "bind this value to this target only if the<br>
value currently bound to the target nominally doesn't exist"?<br>
<br>
To be clear, this would be a *really* big addition to the language<br>
that would have significant long term ramifications for how the<br>
language gets taught to new developers.<br>
<br>
At the same time, asking whether or not an object represents an<br>
absence of data rather than the truth of a proposition seems to me<br>
like a sufficiently common problem in a wide enough variety of domains<br>
that it may be worth elevating to the level of giving it dedicated<br>
syntactic support.<br>
<br>
Regards,<br>
Nick.<br>
<br>
Rendered HTML version: <a href="https://www.python.org/dev/peps/pep-0531/" rel="noreferrer" target="_blank">https://www.python.org/dev/<wbr>peps/pep-0531/</a><br>
==============================<wbr>=<br>
<br>
PEP: 531<br>
Title: Existence checking operators<br>
Version: $Revision$<br>
Last-Modified: $Date$<br>
Author: Nick Coghlan <<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>><br>
Status: Draft<br>
Type: Standards Track<br>
Content-Type: text/x-rst<br>
Created: 25-Oct-2016<br>
Python-Version: 3.7<br>
Post-History: 28-Oct-2016<br>
<br>
Abstract<br>
========<br>
<br>
Inspired by PEP 505 and the related discussions, this PEP proposes the addition<br>
of two new control flow operators to Python:<br>
<br>
* Existence-checking precondition ("exists-then"): ``expr1 ?then expr2``<br>
* Existence-checking fallback ("exists-else"): ``expr1 ?else expr2``<br>
<br>
as well as the following abbreviations for common existence checking<br>
expressions and statements:<br>
<br>
* Existence-checking attribute access:<br>
``obj?.attr`` (for ``obj ?then obj.attr``)<br>
* Existence-checking subscripting:<br>
``obj?[expr]`` (for ``obj ?then obj[expr]``)<br>
* Existence-checking assignment:<br>
``value ?= expr`` (for ``value = value ?else expr``)<br>
<br>
The common ``?`` symbol in these new operator definitions indicates that they<br>
use a new "existence checking" protocol rather than the established<br>
truth-checking protocol used by if statements, while loops, comprehensions,<br>
generator expressions, conditional expressions, logical conjunction, and<br>
logical disjunction.<br>
<br>
This new protocol would be made available as ``operator.exists``, with the<br>
following characteristics:<br>
<br>
* types can define a new ``__exists__`` magic method (Python) or<br>
``tp_exists`` slot (C) to override the default behaviour. This optional<br>
method has the same signature and possible return values as ``__bool__``.<br>
* ``operator.exists(None)`` returns ``False``<br>
* ``operator.exists(<wbr>NotImplemented)`` returns ``False``<br>
* ``operator.exists(Ellipsis)`` returns ``False``<br>
* ``float``, ``complex`` and ``decimal.Decimal`` will override the existence<br>
check such that ``NaN`` values return ``False`` and other values (including<br>
zero values) return ``True``<br>
* for any other type, ``operator.exists(obj)`` returns True by default. Most<br>
importantly, values that evaluate to False in a truth checking context<br>
(zeroes, empty containers) will still evaluate to True in an existence<br>
checking context<br>
<br>
<br>
Relationship with other PEPs<br>
============================<br>
<br>
While this PEP was inspired by and builds on Mark Haase's excellent work in<br>
putting together PEP 505, it ultimately competes with that PEP due to<br>
significant differences in the specifics of the proposed syntax and semantics<br>
for the feature.<br>
<br>
It also presents a different perspective on the rationale for the change by<br>
focusing on the benefits to existing Python users as the typical demands of<br>
application and service development activities are genuinely changing. It<br>
isn't an accident that similar features are now appearing in multiple<br>
programming languages, and while it's a good idea for us to learn from how other<br>
language designers are handling the problem, precedents being set elsewhere<br>
are more relevant to *how* we would go about tackling this problem than they<br>
are to whether or not we think it's a problem we should address in the first<br>
place.<br>
<br>
<br>
Rationale<br>
=========<br>
<br>
Existence checking expressions<br>
------------------------------<br>
<br>
An increasingly common requirement in modern software development is the need<br>
to work with "semi-structured data": data where the structure of the data is<br>
known in advance, but pieces of it may be missing at runtime, and the software<br>
manipulating that data is expected to degrade gracefully (e.g. by omitting<br>
results that depend on the missing data) rather than failing outright.<br>
<br>
Some particularly common cases where this issue arises are:<br>
<br>
* handling optional application configuration settings and function parameters<br>
* handling external service failures in distributed systems<br>
* handling data sets that include some partial records<br>
<br>
It is the latter two cases that are the primary motivation for this PEP - while<br>
needing to deal with optional configuration settings and parameters is a design<br>
requirement at least as old as Python itself, the rise of public cloud<br>
infrastructure, the development of software systems as collaborative networks<br>
of distributed services, and the availability of large public and private data<br>
sets for analysis means that the ability to degrade operations gracefully in<br>
the face of partial service failures or partial data availability is becoming<br>
an essential feature of modern programming environments.<br>
<br>
At the moment, writing such software in Python can be genuinely awkward, as<br>
your code ends up littered with expressions like:<br>
<br>
* ``value1 = expr1.field.of.interest if expr1 is not None else None``<br>
* ``value2 = expr2["field"]["of"]["<wbr>interest"] if expr2 is not None else None``<br>
* ``value3 = expr3 if expr3 is not None else expr4 if expr4 is not<br>
None else expr5``<br>
<br>
If these are only occasional, then expanding out to full statement forms may<br>
help improve readability, but if you have 4 or 5 of them in a row (which is a<br>
fairly common situation in data transformation pipelines), then replacing them<br>
with 16 or 20 lines of conditional logic really doesn't help matters.<br>
<br>
Expanding the three examples above that way hopefully helps illustrate that::<br>
<br>
_expr1 = expr1<br>
if _expr1 is not None:<br>
value1 = _expr1.field.of.interest<br>
else:<br>
value1 = None<br>
_expr2 = expr2<br>
if _expr2 is not None:<br>
value2 = _expr2["field"]["of"]["<wbr>interest"]<br>
else:<br>
value2 = None<br>
_expr3 = expr3<br>
if _expr3 is not None:<br>
value3 = _expr3<br>
else:<br>
_expr4 = expr4<br>
if _expr4 is not None:<br>
value3 = _expr4<br>
else:<br>
value3 = expr5<br>
<br>
The combined impact of the proposals in this PEP is to allow the above sample<br>
expressions to instead be written as:<br>
<br>
* ``value1 = expr1?.field.of.interest``<br>
* ``value2 = expr2?["field"]["of"]["<wbr>interest"]``<br>
* ``value3 = expr3 ?else expr4 ?else expr5``<br>
<br>
In these forms, almost all of the information presented to the reader is<br>
immediately relevant to the question "What does this code do?", while the<br>
boilerplate code to handle missing data by passing it through to the output<br>
or falling back to an alternative input, has shrunk to two uses of the ``?``<br>
symbol and two uses of the ``?else`` keyword.<br>
<br>
In the first two examples, the 31 character boilerplate clause<br>
`` if exprN is not None else None`` (minimally 27 characters for a single letter<br>
variable name) has been replaced by a single ``?`` character, substantially<br>
improving the signal-to-pattern-noise ratio of the lines (especially if it<br>
encourages the use of more meaningful variable and field names rather than<br>
making them shorter purely for the sake of expression brevity).<br>
<br>
In the last example, two instances of the 21 character boilerplate,<br>
`` if exprN is not None`` (minimally 17 characters) are replaced with single<br>
characters, again substantially improving the signal-to-pattern-noise ratio.<br>
<br>
Furthermore, each of our 5 "subexpressions of potential interest" is included<br>
exactly once, rather than 4 of them needing to be duplicated or pulled out<br>
to a named variable in order to first check if they exist.<br>
<br>
The existence checking precondition operator is mainly defined to provide a<br>
clear conceptual basis for the existence checking attribute access and<br>
subscripting operators:<br>
<br>
* ``obj?.attr`` is roughly equivalent to ``obj ?then obj.attr``<br>
* ``obj?[expr]``is roughly equivalent to ``obj ?then obj[expr]``<br>
<br>
The main semantic difference between the shorthand forms and their expanded<br>
equivalents is that the common subexpression to the left of the existence<br>
checking operator is evaluated only once in the shorthand form (similar to<br>
the benefit offered by augmented assignment statements).<br>
<br>
<br>
Existence checking assignment<br>
-----------------------------<br>
<br>
Existence-checking assignment is proposed as a relatively straightforward<br>
expansion of the concepts in this PEP to also cover the common configuration<br>
handling idiom:<br>
<br>
* ``value = value if value is not None else expensive_default()``<br>
<br>
by allowing that to instead be abbreviated as:<br>
<br>
* ``value ?= expensive_default()``<br>
<br>
This is mainly beneficial when the target is a subscript operation or<br>
subattribute, as even without this specific change, the PEP would still<br>
permit this idiom to be updated to:<br>
<br>
* ``value = value ?else expensive_default()``<br>
<br>
The main argument *against* adding this form is that it's arguably ambiguous<br>
and could mean either:<br>
<br>
* ``value = value ?else expensive_default()``; or<br>
* ``value = value ?then value.subfield.of.interest``<br>
<br>
The second form isn't at all useful, but if this concern was deemed significant<br>
enough to address while still keeping the augmented assignment feature,<br>
the full keyword could be included in the syntax:<br>
<br>
* ``value ?else= expensive_default()``<br>
<br>
Alternatively, augmented assignment could just be dropped from the current<br>
proposal entirely and potentially reconsidered at a later date.<br>
<br>
<br>
Existence checking protocol<br>
---------------------------<br>
<br>
The existence checking protocol is including in this proposal primarily to<br>
allow for proxy objects (e.g. local representations of remote resources) and<br>
mock objects used in testing to correctly indicate non-existence of target<br>
resources, even though the proxy or mock object itself is not None.<br>
<br>
However, with that protocol defined, it then seems natural to expand it to<br>
provide a type independent way of checking for ``NaN`` values in numeric types<br>
- at the moment you need to be aware of the exact data type you're working with<br>
(e.g. builtin floats, builtin complex numbers, the decimal module) and use the<br>
appropriate operation (e.g. ``math.isnan``, ``cmath.isnan``,<br>
``decimal.getcontext().is_nan(<wbr>)``, respectively)<br>
<br>
Similarly, it seems reasonable to declare that the other placeholder builtin<br>
singletons, ``Ellipsis`` and ``NotImplemented``, also qualify as objects that<br>
represent the absence of data moreso than they represent data.<br>
<br>
<br>
Proposed symbolic notation<br>
--------------------------<br>
<br>
Python has historically only had one kind of implied boolean context: truth<br>
checking, which can be invoked directly via the ``bool()`` builtin. As this PEP<br>
proposes a new kind of control flow operation based on existence checking rather<br>
than truth checking, it is considered valuable to have a reminder directly<br>
in the code when existence checking is being used rather than truth checking.<br>
<br>
The mathematical symbol for existence assertions is U+2203 'THERE EXISTS': ``∃``<br>
<br>
Accordingly, one possible approach to the syntactic additions proposed in this<br>
PEP would be to use that already defined mathematical notation:<br>
<br>
* ``expr1 ∃then expr2``<br>
* ``expr1 ∃else expr2``<br>
* ``obj∃.attr``<br>
* ``obj∃[expr]``<br>
* ``target ∃= expr``<br>
<br>
However, there are two major problems with that approach, one practical, and<br>
one pedagogical.<br>
<br>
The practical problem is the usual one that most keyboards don't offer any easy<br>
way of entering mathematical symbols other than those used in basic arithmetic<br>
(even the symbols appearing in this PEP were ultimately copied & pasted<br>
from [3]_ rather than being entered directly).<br>
<br>
The pedagogical problem is that the symbols for existence assertions (``∃``)<br>
and universal assertions (``∀``) aren't going to be familiar to most people<br>
the way basic arithmetic operators are, so we wouldn't actually be making the<br>
proposed syntax easier to understand by adopting ``∃``.<br>
<br>
By contrast, ``?`` is one of the few remaining unused ASCII punctuation<br>
characters in Python's syntax, making it available as a candidate syntactic<br>
marker for "this control flow operation is based on an existence check, not a<br>
truth check".<br>
<br>
Taking that path would also have the advantage of aligning Python's syntax<br>
with corresponding syntax in other languages that offer similar features.<br>
<br>
Drawing from the existing summary in PEP 505 and the Wikipedia articles on<br>
the "safe navigation operator [1]_ and the "null coalescing operator" [2]_,<br>
we see:<br>
<br>
* The ``?.`` existence checking attribute access syntax precisely aligns with:<br>
<br>
* the "safe navigation" attribute access operator in C# (``?.``)<br>
* the "optional chaining" operator in Swift (``?.``)<br>
* the "safe navigation" attribute access operator in Groovy (``?.``)<br>
* the "conditional member access" operator in Dart (``?.``)<br>
<br>
* The ``?[]`` existence checking attribute access syntax precisely aligns with:<br>
<br>
* the "safe navigation" subscript operator in C# (``?[]``)<br>
* the "optional subscript" operator in Swift (``?[].``)<br>
<br>
* The ``?else`` existence checking fallback syntax semantically aligns with:<br>
<br>
* the "null-coalescing" operator in C# (``??``)<br>
* the "null-coalescing" operator in PHP (``??``)<br>
* the "nil-coalescing" operator in Swift (``??``)<br>
<br>
To be clear, these aren't the only spelling of these operators used in other<br>
languages, but they're the most common ones, and the ``?`` symbol is the most<br>
common syntactic marker by far (presumably prompted by the use of ``?`` to<br>
introduce the "then" clause in C-style conditional expressions, which many<br>
of these languages also offer).<br>
<br>
<br>
Proposed keywords<br>
-----------------<br>
<br>
Given the symbolic marker ``?``, it would be syntactically unambiguous to spell<br>
the existence checking precondition and fallback operations using the same<br>
keywords as their truth checking counterparts:<br>
<br>
* ``expr1 ?and expr2`` (instead of ``expr1 ?then expr2``)<br>
* ``expr1 ?or expr2`` (instead of ``expr1 ?else expr2``)<br>
<br>
However, while syntactically unambiguous when written, this approach makes<br>
the code incredibly hard to *pronounce* (What's the pronunciation of "?"?) and<br>
also hard to *describe* (given reused keywords, there's no obvious shorthand<br>
terms for "existence checking precondition (?and)" and "existence checking<br>
fallback (?or)" that would distinguish them from "logical conjunction (and)"<br>
and "logical disjunction (or)").<br>
<br>
We could try to encourage folks to pronounce the ``?`` symbol as "exists",<br>
making the shorthand names the "exists-and expression" and the<br>
"exists-or expression", but there'd be no way of guessing those names purely<br>
from seeing them written in a piece of code.<br>
<br>
Instead, this PEP takes advantage of the proposed symbolic syntax to introduce<br>
a new keyword (``?then``) and borrow an existing one (``?else``) in a way<br>
that allows people to refer to "then expressions" and "else expressions"<br>
without ambiguity.<br>
<br>
These keywords also align well with the conditional expressions that are<br>
semantically equivalent to the proposed expressions.<br>
<br>
For ``?else`` expressions, ``expr1 ?else expr2`` is equivalent to::<br>
<br>
_lhs_result = expr1<br>
_lhs_result if operator.exists(_lhs_result) else expr2<br>
<br>
Here the parallel is clear, since the ``else expr2`` appears at the end of<br>
both the abbreviated and expanded forms.<br>
<br>
For ``?then`` expressions, ``expr1 ?then expr2`` is equivalent to::<br>
<br>
_lhs_result = expr1<br>
expr2 if operator.exists(_lhs_result) else _lhs_result<br>
<br>
Here the parallel isn't as immediately obvious due to Python's traditionally<br>
anonymous "then" clauses (introduced by ``:`` in ``if`` statements and suffixed<br>
by ``if`` in conditional expressions), but it's still reasonably clear as long<br>
as you're already familiar with the "if-then-else" explanation of conditional<br>
control flow.<br>
<br>
<br>
Risks and concerns<br>
==================<br>
<br>
Readability<br>
-----------<br>
<br>
Learning to read and write the new syntax effectively mainly requires<br>
internalising two concepts:<br>
<br>
* expressions containing ``?`` include an existence check and may short circuit<br>
* if ``None`` or another "non-existent" value is an expected input, and the<br>
correct handling is to propagate that to the result, then the existence<br>
checking operators are likely what you want<br>
<br>
Currently, these concepts aren't explicitly represented at the language level,<br>
so it's a matter of learning to recognise and use the various idiomatic<br>
patterns based on conditional expressions and statements.<br>
<br>
<br>
Magic syntax<br>
------------<br>
<br>
There's nothing about ``?`` as a syntactic element that inherently suggests<br>
``is not None`` or ``operator.exists``. The main current use of ``?`` as a<br>
symbol in Python code is as a trailing suffix in IPython environments to<br>
request help information for the result of the preceding expression.<br>
<br>
However, the notion of existence checking really does benefit from a pervasive<br>
visual marker that distinguishes it from truth checking, and that calls for<br>
a single-character symbolic syntax if we're going to do it at all.<br>
<br>
<br>
Conceptual complexity<br>
---------------------<br>
<br>
This proposal takes the currently ad hoc and informal concept of "existence<br>
checking" and elevates it to the status of being a syntactic language feature<br>
with a clearly defined operator protocol.<br>
<br>
In many ways, this should actually *reduce* the overall conceptual complexity<br>
of the language, as many more expectations will map correctly between truth<br>
checking with ``bool(expr)`` and existence checking with<br>
``operator.exists(expr)`` than currently map between truth checking and<br>
existence checking with ``expr is not None`` (or ``expr is not NotImplemented``<br>
in the context of operand coercion, or the various NaN-checking operations<br>
in mathematical libraries).<br>
<br>
As a simple example of the new parallels introduced by this PEP, compare::<br>
<br>
all_are_true = all(map(bool, iterable))<br>
at_least_one_is_true = any(map(bool, iterable))<br>
all_exist = all(map(operator.exists, iterable))<br>
at_least_one_exists = any(map(operator.exists, iterable))<br>
<br>
<br>
Design Discussion<br>
=================<br>
<br>
Subtleties in chaining existence checking expressions<br>
------------------------------<wbr>-----------------------<br>
<br>
Similar subtleties arise in chaining existence checking expressions as already<br>
exist in chaining logical operators: the behaviour can be surprising if the<br>
right hand side of one of the expressions in the chain itself returns a<br>
value that doesn't exist.<br>
<br>
As a result, ``value = arg1 ?then f(arg1) ?else default()`` would be dubious for<br>
essentially the same reason that ``value = cond and expr1 or expr2`` is dubious:<br>
the former will evaluate ``default()`` if ``f(arg1)`` returns ``None``, just<br>
as the latter will evaluate ``expr2`` if ``expr1`` evaluates to ``False`` in<br>
a boolean context.<br>
<br>
<br>
Ambiguous interaction with conditional expressions<br>
------------------------------<wbr>--------------------<br>
<br>
In the proposal as currently written, the following is a syntax error:<br>
<br>
* ``value = f(arg) if arg ?else default``<br>
<br>
While the following is a valid operation that checks a second condition if the<br>
first doesn't exist rather than merely being false:<br>
<br>
* ``value = expr1 if cond1 ?else cond2 else expr2``<br>
<br>
The expression chaining problem described above means that the argument can be<br>
made that the first operation should instead be equivalent to:<br>
<br>
* ``value = f(arg) if operator.exists(arg) else default``<br>
<br>
requiring the second to be written in the arguably clearer form:<br>
<br>
* ``value = expr1 if (cond1 ?else cond2) else expr2``<br>
<br>
Alternatively, the first form could remain a syntax error, and the existence<br>
checking symbol could instead be attached to the ``if`` keyword:<br>
<br>
* ``value = expr1 if? cond else expr2``<br>
<br>
<br>
Existence checking in other truth-checking contexts<br>
------------------------------<wbr>---------------------<br>
<br>
The truth-checking protocol is currently used in the following syntactic<br>
constructs:<br>
<br>
* logical conjunction (and-expressions)<br>
* logical disjunction (or-expressions)<br>
* conditional expressions (if-else expressions)<br>
* if statements<br>
* while loops<br>
* filter clauses in comprehensions and generator expressions<br>
<br>
In the current PEP, switching from truth-checking with ``and`` and ``or`` to<br>
existence-checking is a matter of substituting in the new keywords, ``?then``<br>
and ``?else`` in the appropriate places.<br>
<br>
For other truth-checking contexts, it proposes either importing and<br>
using the ``operator.exists`` API, or else continuing with the current idiom<br>
of checking specifically for ``expr is not None`` (or the context appropriate<br>
equivalent).<br>
<br>
The simplest possible enhancement in that regard would be to elevate the<br>
proposed ``exists()`` API from an operator module function to a new builtin<br>
function.<br>
<br>
Alternatively, the ``?`` existence checking symbol could be supported as a<br>
modifier on the ``if`` and ``while`` keywords to indicate the use of an<br>
existence check rather than a truth check.<br>
<br>
However, it isn't at all clear that the potential consistency benefits gained<br>
for either suggestion would justify the additional disruption, so they've<br>
currently been omitted from the proposal.<br>
<br>
<br>
Defining expected invariant relations between ``__bool__`` and ``__exists__``<br>
------------------------------<wbr>------------------------------<wbr>-----------------<br>
<br>
The PEP currently leaves the definition of ``__bool__`` on all existing types<br>
unmodified, which ensures the entire proposal remains backwards compatible,<br>
but results in the following cases where ``bool(obj)`` returns ``True``, but<br>
the proposed ``operator.exists(obj)`` would return ``False``:<br>
<br>
* ``NaN`` values for ``float``, ``complex``, and ``decimal.Decimal``<br>
* ``Ellipsis``<br>
* ``NotImplemented``<br>
<br>
The main argument for potentially changing these is that it becomes easier to<br>
reason about potential code behaviour if we have a recommended invariant in<br>
place saying that values which indicate they don't exist in an existence<br>
checking context should also report themselves as being ``False`` in a truth<br>
checking context.<br>
<br>
Failing to define such an invariant would lead to arguably odd outcomes like<br>
``float("NaN") ?else 0.0`` returning ``0.0`` while ``float("NaN") or 0.0``<br>
returns ``NaN``.<br>
<br>
<br>
Limitations<br>
===========<br>
<br>
Arbitrary sentinel objects<br>
--------------------------<br>
<br>
This proposal doesn't attempt to provide syntactic support for the "sentinel<br>
object" idiom, where ``None`` is a permitted explicit value, so a<br>
separate sentinel object is defined to indicate missing values::<br>
<br>
_SENTINEL = object()<br>
def f(obj=_SENTINEL):<br>
return obj if obj is not _SENTINEL else default_value()<br>
<br>
This could potentially be supported at the expense of making the existence<br>
protocol definition significantly more complex, both to define and to use:<br>
<br>
* at the Python layer, ``operator.exists`` and ``__exists__`` implementations<br>
would return the empty tuple to indicate non-existence, and otherwise return<br>
a singleton tuple containing a reference to the object to be used as the<br>
result of the existence check<br>
* at the C layer, ``tp_exists`` implementations would return NULL to indicate<br>
non-existence, and otherwise return a `PyObject *` pointer as the<br>
result of the existence check<br>
<br>
Given that change, the sentinel object idiom could be rewritten as::<br>
<br>
class Maybe:<br>
SENTINEL = object()<br>
def __init__(self, value):<br>
self._result = (value,) is value is not self.SENTINEL else ()<br>
def __exists__(self):<br>
return self._result<br>
<br>
def f(obj=Maybe.SENTINEL):<br>
return Maybe(obj) ?else default_value()<br>
<br>
However, I don't think cases where the 3 proposed standard sentinel values (i.e.<br>
``None``, ``Ellipsis`` and ``NotImplemented``) can't be used are going to be<br>
anywhere near common enough for the additional protocol complexity and the loss<br>
of symmetry between ``__bool__`` and ``__exists__`` to be worth it.<br>
<br>
<br>
Specification<br>
=============<br>
<br>
The Abstract already gives the gist of the proposal and the Rationale gives<br>
some specific examples. If there's enough interest in the basic idea, then a<br>
full specification will need to provide a precise correspondence between the<br>
proposed syntactic sugar and the underlying conditional expressions that is<br>
sufficient to guide the creation of a reference implementation.<br>
<br>
...TBD...<br>
<br>
<br>
Implementation<br>
==============<br>
<br>
As with PEP 505, actual implementation has been deferred pending in-principle<br>
interest in the idea of adding these operators - the implementation isn't<br>
the hard part of these proposals, the hard part is deciding whether or not<br>
this is a change where the long term benefits for new and existing Python users<br>
outweigh the short term costs involved in the wider ecosystem (including<br>
developers of other implementations, language curriculum developers, and<br>
authors of other Python related educational material) adjusting to the change.<br>
<br>
...TBD...<br>
<br>
<br>
References<br>
==========<br>
<br>
.. [1] Wikipedia: Safe navigation operator<br>
(<a href="https://en.wikipedia.org/wiki/Safe_navigation_operator" rel="noreferrer" target="_blank">https://en.wikipedia.org/<wbr>wiki/Safe_navigation_operator</a>)<br>
<br>
.. [2] Wikipedia: Null coalescing operator<br>
(<a href="https://en.wikipedia.org/wiki/Null_coalescing_operator" rel="noreferrer" target="_blank">https://en.wikipedia.org/<wbr>wiki/Null_coalescing_operator</a>)<br>
<br>
.. [3] FileFormat.info: Unicode Character 'THERE EXISTS' (U+2203)<br>
(<a href="http://www.fileformat.info/info/unicode/char/2203/index.htm" rel="noreferrer" target="_blank">http://www.fileformat.info/<wbr>info/unicode/char/2203/index.<wbr>htm</a>)<br>
<br>
<br>
Copyright<br>
=========<br>
<br>
This document has been placed in the public domain under the terms of the<br>
CC0 1.0 license: <a href="https://creativecommons.org/publicdomain/zero/1.0/" rel="noreferrer" target="_blank">https://creativecommons.org/<wbr>publicdomain/zero/1.0/</a><br>
<br>
<br>
..<br>
Local Variables:<br>
mode: indented-text<br>
indent-tabs-mode: nil<br>
sentence-end-double-space: t<br>
fill-column: 70<br>
coding: utf-8<br>
End:<br>
<span class="HOEnZb"><font color="#888888"><br>
<br>
<br>
<br>
--<br>
Nick Coghlan | <a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a> | Brisbane, Australia<br>
______________________________<wbr>_________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/<wbr>codeofconduct/</a></font></span></blockquote></div><br></div>