On 2020-03-02 4:03 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 09:26, Soni L. <fakedme...@gmail.com> wrote:
On 2020-03-02 2:04 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 08:40, Soni L. <fakedme...@gmail.com> wrote:
> > All operations on None should raise a NoneError,
So every function in every type implemented in C or in Python,
whether part of Python or third-party, that has code like this:
if not isisntance(arg, numbers.Integral):
raise TypeError(f"can only spam integers, not '{arg!r}'")
… has to change to test if arg is None and raise a different error.
Otherwise, you’re not going to catch the error from + in your
example if g.foo(h) is None.
None can have __radd__ or whatnot as well, no? (read: please don't
directly raise TypeError, [redacted].)
That will help some types, but not all, with +.
But, more importantly, that only helps with (reversible) operators. It
does nothing for int(x) raising TypeError when x is not
string/byteslike/number/something with __int__. Or when it doesn’t
meet the (different) requirements for the first or the second argument
of int.from_bytes. And so on for a bunch of other methods and class
methods. And that’s just one type; the same is true for lots of other
types, and plain old functions—hundreds just in the builtins, zillions
in third-party code.
The fact that you have a partial solution for a tiny subset of the
problem doesn’t help. If you want to make all operations that raise
TypeError on None instead raise NoneError, you need to come up with a
way to do that, and I don’t see any way that doesn’t involve rewriting
zillions of lines of code both in Python itself and in third-party
libraries and applications.
> which should be a TypeError for backwards compatibility.
But the most common errors caused by not checking for None are
AttributeError. If you turn these into a subclass of TypeError, that
won’t be backward compatible. Others are ValueError, and that won’t
be backward compatible either.
Hm. So, my original idea was to have a NoneError and MI (Multiple
Inheritance) it with TypeError, ValueError, LookupError,
AttributeError, etc. Then I checked "None[1]" and found that it
raised TypeError, so I thought all of None's unimplemented semantics
provided TypeError, and didn't realize attribute lookup was
different. Oh well .-.
This is a pretty serious problem with the proposal. Do you have an
answer beyond “oh well”, or does that mean you’re giving up on the
idea, or that you think we should go ahead with the idea even though
we know it can’t work, or what?
Where does None do ValueError, tho? I haven't seen that one.
The most recent ones I’ve seen came from NumPy and TensorFlow, both of
which raise ValueError from some methods if you have an array of type
object and have any None values, with a message like "ValueError: None
values not supported." I’m sure there are others; this is just the
first one that occurred to me.
When I was thinking about this I was thinking of the direct operations
like attribute, indexing, etc, so what if we don't bother with TypeError
and ValueError that aren't related to direct operations on None?
e.g.:
adding a None with something, or having None added to something, are
direct operations on None -> raise.
int(None) is *NOT* a direct operation on None -> doesn't raise. (but may
still raise NoneError if we decide to do so through __int__. but
honestly I kinda feel like __int__ is a mistake/wart and might make a
separate post about this if there's interest.)
indexing a None is a direct operation on None -> raise.
indexing *with* a None is *NOT* a direct operation on None - doesn't
raise. (this also solves some of the problems you listed below)
etc.
This seems more in-line with Python in general, and now I regret saying
that "All operations on None should raise a NoneError" because I didn't
realize how broad that actually was.
So to refine it down a bit: "All direct operations on None, such as
attribute access, indexing and mathematical operations, but not integer
conversion, should raise a NoneError". This includes weird ones like
"await None" and "yield from None" and perhaps even "with None". Does
this sound better?
[... hm, after writing this I realized I said "operations *on* None",
not "operations *with* None". it seems weird to consider "int(None)" or
"{}[None]" an operation *on* None. while they're indeed operations
*with* None, I wouldn't claim that e.g. "[].append({})" is an operation
*on* a dict just because there's a dict, so why should I do so with
None? I'd honestly say this one is on you for misunderstanding me :v]
> we can then look into merging the proposals of None-aware operators and Exception-aware
operators such that the Exception-aware operators fallback to
NoneError if no exception type is provided.
How are you going to provide an exception type for most of the
None-aware operators? For example, in a?[b][c]?[d], where do the
exception types go? Without solving that, how do you merge the two
proposals?
> we should also look into making "except" default to using NoneError instead of
BaseException, with a future flag.
Why? That sounds like a terrible idea. Most uses of bare except are
bad code, but that doesn’t mean spuriously breaking all of that code
and forcing people to deal with a problem that may never have come
up in their production code is a good idea. And meanwhile, the good
uses of bare except—quick&dirty code at the REPL, exception handlers
that access the current exception through other means, etc.—would
all break. And what code would benefit?
I don't know if these are good ideas, that's why I used the
expression "look into".
OK, but surely you must have some idea for why it might be useful or
you wouldn’t have suggested looking into it? So what is that idea?
I have a feeling some ppl would appreciate one or the other (or both),
so I figured it was a good idea to mention the possibility.
fwiw, assuming we had exception-aware operators, I believe
a?[b][c]?[d] wouldn't be possible, but I know a?[b]?[c]?[d] would
become a[b][c][d]?:None (or a[b][c][d]?NoneError:None to be explicit.)
Even in that case it’s still not the same thing. For example, if any
of b, c, or d is None, the none-aware operators will still raise, but
your exception-aware version will not.
So, the fact that exception-aware operators could replace some but not
most uses of none-aware operators, and would be inaccurate even when
they can be used, doesn’t seem very promising.
All in all, this whole NoneError thing seems like it could be a useful
design for a brand-new Python-like language, but I can’t see how it
can be retrofitted usefully into Python.
This might well be true! I searched around and couldn't find anything
about a hypothetical new NoneError exception, so I figured it probably
hasn't been posted before and thought it was worth a shot.
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/JI2KKXLLNR3OU3WETYQXL5FV574L22E4/
Code of Conduct: http://python.org/psf/codeofconduct/