On Mon, Jul 23, 2018 at 7:46 AM, Grégory Lielens <gregory.lielens@gmail.com> wrote:
 Paul Moore wrote:

This is my impression, as well. It seems like something that's helpful
in dealing with unstructured object hierarchies with lots of optional
attributes - which is where JSON tends to be used.

But given that, I'm really much more interested in seeing the new
operators compared against a well-written "JSON object hierarchy
traversal" library than against raw Python code.


Good point, I did not think about that when suggesting to give some context, but indeed if it's linked to a library in particular, there is always the possibility to choose another object than None as the "nothing here" marker.

From what I gather, disallowing reassignment all but cements None as the "nothing here" marker (the originally intent?), _especially_ when "nothing here" is candidate for replacement by a more appropriate, type- or domain- or context-specific "nothing here" marker. Imbuing None with any other meaning brings nothing but headache. It's an attractive nuisance. None is None is None, there can be only one! You don't know what it is, only that it's not anything else. What possible meaning can such an object usefully have in an application built on disparate libraries with their own ideas on the matter?

Every API I've used (apologies for coming up blank on a concrete example!) granting None meaning is awkward to consume. `.get()` interfaces are less useful (must carry your own internal sentinels) or more try/except blocks are required to achieve the same end (not a bad thing per se, but a diminished experience to be sure). Early APIs I wrote in this "well it's None, but but but, with this convenient meaning, see?! SEE??"-style were later regarded -- quite thoroughly -- as a bad idea by myself, my peers, and downstream consumers.

I'd personally use these new operators both frequently and judiciously. They align very well with a "set 'em up and knock 'em down"-style I use: normalized, progressively conditioned input values fast-followed by aggressive short-circuits and clear early returns. IME this pattern generates clean, readable, and performant code. Honestly the short-circuiting capability alone is enough to sell me :-) This PEP would find legitimate use by me every day. I'm not 100% sold on `?[` (then again, attributes are pulled from an object namespace via `?.` and namespaces are containers by definition) but `?.` and `??` deliver immense value.

Not sure if useful, but this discussion reminds me of a pattern prevalent in the Elixir community. They use `?` and `!` in function definitions to denote variants differing only on return behavior (not function clauses! This is by convention only, they're entirely new functions with a symbol in their name). It looks something like this:

# Default function.
# Return a tuple {interesting-or-nil, error-or-nil}.
def open(path) do ... end

# Maybe variant.
# Return a boolean, or less often, interesting-or-nil (replaces `is_` or `can_` methods in Python).
def open?(path) do ... end

# Forceful variant.
# Return interesting or die trying (inverse of `.get()` methods in Python; raising is not the default expectation in Elixir).
def open!(path) do ... end

The `?.`-operator reminds me of this. It offers to perform an extremely common operation (simple attribute access) while short-circuiting on the most frequently triggered guard condition (AttributeError).

I don't think the utility here is restricted to deeply nested JSON `loads()` or one-off scripts. It better aligns the community on semantics, encouraging more idiomatic -- and uniform! -- interactions with None.
 
--

C Anthony