On 29 October 2016 at 14:52, Nick Coghlan firstname.lastname@example.org 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 = 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.