The readability issue isn't just a matter of a new set of symbols to learn.
It isn't even that the symbols are used in a non-intuitive way (though that
doesn't help). It's not even that the question mark, unlike the dot, is
very visually obtrusive (which also doesn't help). It also screws with the
grammar in an unexpected way. I would be fine with something like:

def func(data=None):
    data ?= []
    ...

If there were some corresponding dunder method to implement that behavior,
but it's not clear to me that such a thing is possible. If it isn't, that
would make `?=` (or '??=' though I don't see the need for double '?'s) a
special statement, a special case that someone who's familiar with how
other in-place operators work, would be thrown for a loop. The other use
cases look like they break down in a different manner than they actually do:

person?.name

looks like it would break down similar to

person().name

but it doesn't because '?' by itself is not an operator.
similarly:

person?[0]

looks like it would break down similar to

person()[0]

but again, the operator is '?[...]' not '?'

This also raises the question of other operators that could be made null
aware. Why not:

func?()

?-num

?~num

num ?+ other

Why not other reflective operators?

num ?+= other

I think this could be helped by simply making '?' an operator with a very
high order of operations, so that pretty-much any expression on the LHS is
guarded for 'NoneType' AttributeErrors:

val = person[0]?
val = person.name?

That has the double benefit of making multiple '?.' or '?[]' operations
unnecessary. I mean the following expression would almost never make sense
under the current proposal:

instead of:

val = person?.name?[0]?.lower()

you could write:

val = person.name[0].lower()?

That also aligns a little better with common usage of '?' in natural
language.

But again; there doesn't seem to be a reasonable way to implement a
corresponding dunder method for those operators because they're special.
I also disagree with the idea that None is special enough to warrant such
special behavior. There are other special None-like objects (as David Mertz
pointed out) like NaN and custom place-holders for cases where None is a
valid argument.

Yet another possibility is to make '?' signify a parameter who's default
value is an expression that should be evaluated when necessary:

def func(data: List = []?):
    ...

That gets closer to another often requested feature: delayed expression
evaluation (as Steve D'Aprano pointed out).
So there are other, possibly better uses for the '?' symbol. It's something
that needs to be carefully considered.


On Wed, Jul 25, 2018 at 10:09 PM, David Mertz <me...@gnosis.cx> wrote:

> On Wed, Jul 25, 2018 at 10:50 PM Nicholas Chammas <
> nicholas.cham...@gmail.com> wrote:
>
>> Indeed. Thanks for the counter-example. I think the correct translation
>> is as follows:
>>     food = spam?.eggs?.bacon
>> Becomes:
>>     food = None
>>     if spam is not None and spam.eggs is not None:
>>         food = spam.eggs.bacon
>>
> Did I get it right now? :)
>>
>
> Nope, still not right, I'm afraid!
>
> Chris Angelica provided a more accurate translation.  Do you not see that
> the fact that your *second* try at understanding the actual behavior is
> still wrong suggest that this operator is a HUGE bug magnet?!
>
>
>> So, shame on me. I think this particular mistake reflects more on me than
>> on PEP 505, but I see how this kind of mistake reflects badly on the folks
>> advocating for the PEP (or at least, playing devil's advocate).
>>
>
> I really, really don't.  I think you see an intuitive behavior that would
> be nice and useful in a certain area.  That behavior just isn't what the
> PEP proposes though... it's kinda-sorta close enough to be lured into
> thinking it's a good idea.
>
> Honestly, I think the behavior of GreedyAccess in my little library I
> wrote over the last couple nights is FAR more often what programmers
> ACTUALLY want than NullCoalesce is.  Even Steve Dower—in the PEP and in
> this discussion—acknowledges the appeal and utility of the GreedyAccess
> behavior.  It's in the "Rejected Ideas" section, which is fair enough.
>
> But in a library like mine... or indeed, in a much better library that you
> or someone else writes... it's perfectly easy to have both classes, and
> choose which behavior is more useful for your case.  A new syntax feature
> can't let user decide which behavior (or maybe some other behavior
> altogether) is most useful for their specific case.  A library does that
> easily[*].
>
> [*] In version 0.1.1 of coalescing—changed from 0.1—I added the option to
> use a sentinel other than None if you want.  I'm not sure how useful that
> is, but that idea was in some old PEPs, and I think in the Rejected Ideas
> of 505.  With a library, I have a parameter that need not be used to switch
> that[**].  E.g.:
>
> NullCoalesce(foo, sentinel=float('nan')).bar.baz.blam
>
> [**] Yes, I even handle NaN's in a special way because they are non-equal
> even to themselves.  You could use empty string, or 0, or my_null =
> object(), or whatever.
> --
> Keeping medicines from the bloodstreams of the sick; food
> from the bellies of the hungry; books from the hands of the
> uneducated; technology from the underdeveloped; and putting
> advocates of freedom in prisons.  Intellectual property is
> to the 21st century what the slave trade was to the 16th.
>
> _______________________________________________
> Python-ideas mailing list
> Python-ideas@python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
>
>
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to