None should raise a new exception, NoneError
All operations on None should raise a NoneError, which should be a TypeError for backwards compatibility. try: x = a[b][c][d][e][f] + g.foo(h) except NoneError: x = None 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. we should also look into making "except" default to using NoneError instead of BaseException, with a future flag.
On 2020-03-02 1:48 p.m., Steven D'Aprano wrote:
On Mon, Mar 02, 2020 at 01:39:39PM -0300, Soni L. wrote:
we should also look into making "except" default to using NoneError instead of BaseException
We would we want to do that?
"explicit is better than implicit" and also for consistency with the hypothetical exception-aware operators. and also because it's more commonly useful than just catching BaseException. also maybe with just NoneError + new default for "except" we can squash ppl's desires for None-aware (or exception-aware) operators!
On Tue, Mar 3, 2020 at 3:40 AM Soni L. <fakedme+py@gmail.com> wrote:
All operations on None should raise a NoneError, which should be a TypeError for backwards compatibility.
try: x = a[b][c][d][e][f] + g.foo(h) except NoneError: x = None
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.
we should also look into making "except" default to using NoneError instead of BaseException, with a future flag.
What's the advantage? ChrisA
On Mar 2, 2020, at 08:40, Soni L. <fakedme+py@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.
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.
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?
On 2020-03-02 2:04 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 08:40, Soni L. <fakedme+py@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].)
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 .-. Where does None do ValueError, tho? I haven't seen that one.
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". 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.)
For example, In JavaScript, not defined value will return 'undefined'. This language specification makes language easier to use, but also harder to debug. I think to make NoneErorr (or going to be UndefinedError or something) in languages like JavaScript is nice. Because programmers often don't know value he/she is going to use is undefined. But in Python, we have NameError. And what you want to do can be written with NameError + AttributeErorr. I don't think this proposal is worth changing language so big. Nagata Yamato
On Mar 2, 2020, at 09:26, Soni L. <fakedme+py@gmail.com> wrote:
On 2020-03-02 2:04 p.m., Andrew Barnert wrote: On Mar 2, 2020, at 08:40, Soni L. <fakedme+py@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.
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?
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.
On 2020-03-02 4:03 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 09:26, Soni L. <fakedme+py@gmail.com> wrote:
On 2020-03-02 2:04 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 08:40, Soni L. <fakedme+py@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.
Not sure I like this idea at all, honestly, but: Maybe one of the issues we have is that None is used for multiple things in Python. It’s always some version of “nothing”, but what it specifically means depends on context. For instance, it pretty much always means “use default for keyword” when used as: def fun(x, y=None): If y is None: y = [] ... But it can mean other, somewhat subtler things as well, like totally not defined (as opposed to default) So if it is useful to have a “raising not defined” that had a more specific meaning, maybe a new Singleton is in order: NotDefined, maybe? Then we could give it specific behavior without breaking any code that already uses None. Though I’m not the least bit convinced that the current None behavior is too limiting anyway. -CHB On Mon, Mar 2, 2020 at 1:46 PM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-03-02 4:03 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 09:26, Soni L. <fakedme+py@gmail.com> <fakedme+py@gmail.com> wrote:
On 2020-03-02 2:04 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 08:40, Soni L. <fakedme+py@gmail.com> <fakedme+py@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-leave@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/JI2KKX... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
Anyone can define their own singleton easily enough. None is unique because it's the only built-in general-purpose singleton, and it's too late to change that. I have to agree with Andrew here: in a brand new language it might be a good idea to have a unique exception to be raised for (most) operations not defined on None. But retrofitting this into Python would be too awkward, and it's not worth breaking backward compatibility. (Not even in Python 4.0. :-) On Mon, Mar 2, 2020 at 6:29 PM Christopher Barker <pythonchb@gmail.com> wrote:
Not sure I like this idea at all, honestly, but:
Maybe one of the issues we have is that None is used for multiple things in Python. It’s always some version of “nothing”, but what it specifically means depends on context.
For instance, it pretty much always means “use default for keyword” when used as:
def fun(x, y=None): If y is None: y = [] ...
But it can mean other, somewhat subtler things as well, like totally not defined (as opposed to default)
So if it is useful to have a “raising not defined” that had a more specific meaning, maybe a new Singleton is in order:
NotDefined, maybe?
Then we could give it specific behavior without breaking any code that already uses None.
Though I’m not the least bit convinced that the current None behavior is too limiting anyway.
-CHB
On Mon, Mar 2, 2020 at 1:46 PM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-03-02 4:03 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 09:26, Soni L. <fakedme+py@gmail.com> <fakedme+py@gmail.com> wrote:
On 2020-03-02 2:04 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 08:40, Soni L. <fakedme+py@gmail.com> <fakedme+py@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-leave@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/JI2KKX... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/ZGOW34... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
On Mon, Mar 2, 2020 at 7:12 PM Guido van Rossum <guido@python.org> wrote:
Anyone can define their own singleton easily enough.
Sure, though there is far more utility in having a standard way to do standard things. But if anyone really thinks this is important, making a new Singleton that behaves the way you want to test/prove it’s utility would be the way to go. If it turns out to be useful, see if you XX. Get others to try it out, and then, maybe, propose to add it to Python. It’ll probably not be worth the churn then, but it’s certainly not if it can’t prove its utility. -CHB
On Mon, Mar 2, 2020 at 6:29 PM Christopher Barker <pythonchb@gmail.com> wrote:
Not sure I like this idea at all, honestly, but:
Maybe one of the issues we have is that None is used for multiple things in Python. It’s always some version of “nothing”, but what it specifically means depends on context.
For instance, it pretty much always means “use default for keyword” when used as:
def fun(x, y=None): If y is None: y = [] ...
But it can mean other, somewhat subtler things as well, like totally not defined (as opposed to default)
So if it is useful to have a “raising not defined” that had a more specific meaning, maybe a new Singleton is in order:
NotDefined, maybe?
Then we could give it specific behavior without breaking any code that already uses None.
Though I’m not the least bit convinced that the current None behavior is too limiting anyway.
-CHB
On Mon, Mar 2, 2020 at 1:46 PM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-03-02 4:03 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 09:26, Soni L. <fakedme+py@gmail.com> <fakedme+py@gmail.com> wrote:
On 2020-03-02 2:04 p.m., Andrew Barnert wrote:
On Mar 2, 2020, at 08:40, Soni L. <fakedme+py@gmail.com> <fakedme+py@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-leave@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/JI2KKX... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/ZGOW34...
Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
-- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
This feels a lot like a really verbose way of having nulls-safe operators. On Mon, Mar 2, 2020, 10:40 AM Soni L. <fakedme+py@gmail.com> wrote:
All operations on None should raise a NoneError, which should be a TypeError for backwards compatibility.
try: x = a[b][c][d][e][f] + g.foo(h) except NoneError: x = None
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.
we should also look into making "except" default to using NoneError instead of BaseException, with a future flag. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/FJZFLU... Code of Conduct: http://python.org/psf/codeofconduct/
participants (8)
-
Andrew Barnert
-
Chris Angelico
-
Christopher Barker
-
Guido van Rossum
-
Ryan
-
Soni L.
-
Steven D'Aprano
-
永田大和