PEP 285: Adding a bool type
I offer the following PEP for review by the community. If it receives a favorable response, it will be implemented in Python 2.3. A long discussion has already been held in python-dev about this PEP; most things you could bring up have already been brought up there. The head of the thread there is: http://mail.python.org/pipermail/python-dev/2002-March/020750.html I believe that the review questions listed near the beginning of the PEP are the main unresolved issues from that discussion. This PEP is also on the web, of course, at: http://python.org/peps/pep-0285.html If you prefer to look at code, here's a reasonably complete implementation (in C; it may be slightly out of date relative to the current CVS): http://python.org/sf/528022 --Guido van Rossum (home page: http://www.python.org/~guido/) PEP: 285 Title: Adding a bool type Version: $Revision: 1.12 $ Last-Modified: $Date: 2002/03/30 05:37:02 $ Author: guido@python.org (Guido van Rossum) Status: Draft Type: Standards Track Created: 8-Mar-2002 Python-Version: 2.3 Post-History: 8-Mar-2002, 30-Mar-2002 Abstract This PEP proposes the introduction of a new built-in type, bool, with two constants, False and True. The bool type would be a straightforward subtype (in C) of the int type, and the values False and True would behave like 0 and 1 in most respects (for example, False==0 and True==1 would be true) except repr() and str(). All built-in operations that conceptually return a Boolean result will be changed to return False or True instead of 0 or 1; for example, comparisons, the "not" operator, and predicates like isinstance(). Review Dear reviewers: I'm particularly interested in hearing your opinion about the following three issues: 1) Should this PEP be accepted at all. 2) Should str(True) return "True" or "1": "1" might reduce backwards compatibility problems, but looks strange to me. (repr(True) would always return "True".) 3) Should the constants be called 'True' and 'False' (corresponding to None) or 'true' and 'false' (as in C++, Java and C99). Most other details of the proposal are pretty much forced by the backwards compatibility requirement; e.g. True == 1 and True+1 == 2 must hold, else reams of existing code would break. Minor additional issues: 4) Should we strive to eliminate non-Boolean operations on bools in the future, through suitable warnings, so that e.g. True+1 would eventually (e.g. in Python 3000 be illegal). Personally, I think we shouldn't; 28+isleap(y) seems totally reasonable to me. 5) Should operator.truth(x) return an int or a bool. Tim Peters believes it should return an int because it's been documented as such. I think it should return a bool; most other standard predicates (e.g. issubtype()) have also been documented as returning 0 or 1, and it's obvious that we want to change those to return a bool. Rationale Most languages eventually grow a Boolean type; even C99 (the new and improved C standard, not yet widely adopted) has one. Many programmers apparently feel the need for a Boolean type; most Python documentation contains a bit of an apology for the absence of a Boolean type. I've seen lots of modules that defined constants "False=0" and "True=1" (or similar) at the top and used those. The problem with this is that everybody does it differently. For example, should you use "FALSE", "false", "False", "F" or even "f"? And should false be the value zero or None, or perhaps a truth value of a different type that will print as "true" or "false"? Adding a standard bool type to the language resolves those issues. Some external libraries (like databases and RPC packages) need to be able to distinguish between Boolean and integral values, and while it's usually possible to craft a solution, it would be easier if the language offered a standard Boolean type. The standard bool type can also serve as a way to force a value to be interpreted as a Boolean, which can be used to normalize Boolean values. Writing bool(x) is much clearer than "not not x" and much more concise than if x: return 1 else: return 0 Here are some arguments derived from teaching Python. When showing people comparison operators etc. in the interactive shell, I think this is a bit ugly: >>> a = 13 >>> b = 12 >>> a > b 1 >>> If this was: >>> a > b True >>> it would require one millisecond less thinking each time a 0 or 1 was printed. There's also the issue (which I've seen puzzling even experienced Pythonistas who had been away from the language for a while) that if you see: >>> cmp(a, b) 1 >>> cmp(a, a) 0 >>> you might be tempted to believe that cmp() also returned a truth value. If ints are not (normally) used for Booleans results, this would stand out much more clearly as something completely different. Specification The following Python code specifies most of the properties of the new type: class bool(int): def __new__(cls, val=0): # This constructor always returns an existing instance if val: return True else: return False def __repr__(self): if self: return "True" else: return "False" __str__ = __repr__ def __and__(self, other): if isinstance(other, bool): return bool(int(self) & int(other)) else: return int.__and__(self, other) __rand__ = __and__ def __or__(self, other): if isinstance(other, bool): return bool(int(self) | int(other)) else: return int.__or__(self, other) __ror__ = __or__ def __xor__(self, other): if isinstance(other, bool): return bool(int(self) ^ int(other)) else: return int.__xor__(self, other) __rxor__ = __xor__ # Bootstrap truth values through sheer willpower False = int.__new__(bool, 0) True = int.__new__(bool, 1) The values False and True will be singletons, like None; the C implementation will not allow other instances of bool to be created. At the C level, the existing globals Py_False and Py_True will be appropriated to refer to False and True. All built-in operations that are defined to return a Boolean result will be changed to return False or True instead of 0 or 1. In particular, this affects comparisons (<, <=, ==, !=, >, >=, is, is not, in, not in), the unary operator 'not', the built-in functions callable(), hasattr(), isinstance() and issubclass(), the dict method has_key(), the string and unicode methods endswith(), isalnum(), isalpha(), isdigit(), islower(), isspace(), istitle(), isupper(), and startswith(), the unicode methods isdecimal() and isnumeric(), and the 'closed' attribute of file objects. Note that subclassing from int means that True+1 is valid and equals 2, and so on. This is important for backwards compatibility: because comparisons and so on currently return integer values, there's no way of telling what uses existing applications make of these values. Compatibility Because of backwards compatibility, the bool type lacks many properties that some would like to see. For example, arithmetic operations with one or two bool arguments is allowed, treating False as 0 and True as 1. Also, a bool may be used as a sequence index. I don't see this as a problem, and I don't want evolve the language in this direction either; I don't believe that a stricter interpretation of "Booleanness" makes the language any clearer. Another consequence of the compatibility requirement is that the expression "True and 6" has the value 6, and similarly the expression "False or None" has the value None. The "and" and "or" operators are usefully defined to return the first argument that determines the outcome, and this won't change; in particular, they don't force the outcome to be a bool. Of course, if both arguments are bools, the outcome is always a bool. It can also easily be coerced into being a bool by writing for example "bool(x and y)". Issues Because the repr() or str() of a bool value is different from an int value, some code (for example doctest-based unit tests, and possibly database code that relies on things like "%s" % truth) may fail. How much of a backwards compatibility problem this will be, I don't know. If we this turns out to be a real problem, we could changes the rules so that str() of a bool returns "0" or "1", while repr() of a bool still returns "False" or "True". Other languages (C99, C++, Java) name the constants "false" and "true", in all lowercase. In Python, I prefer to stick with the example set by the existing built-in constants, which all use CapitalizedWords: None, Ellipsis, NotImplemented (as well as all built-in exceptions). Python's built-in module uses all lowercase for functions and types only. But I'm willing to consider the lowercase alternatives if enough people think it looks better. It has been suggested that, in order to satisfy user expectations, for every x that is considered true in a Boolean context, the expression x == True should be true, and likewise if x is considered false, x == False should be true. This is of course impossible; it would mean that e.g. 6 == True and 7 == True, from which one could infer 6 == 7. Similarly, [] == False == None would be true, and one could infer [] == None, which is not the case. I'm not sure where this suggestion came from; it was made several times during the first review period. For truth testing of a value, one should use "if", e.g. "if x: print 'Yes'", not comparison to a truth value; "if x == True: print 'Yes'" is not only wrong, it is also strangely redundant. Implementation An experimental, but fairly complete implementation in C has been uploaded to the SourceForge patch manager: http://python.org/sf/528022 Copyright This document has been placed in the public domain. Local Variables: mode: indented-text indent-tabs-mode: nil fill-column: 70 End:
1) Should this PEP be accepted at all.
I can do without it, but wouldn't mind it, as long as bools don't get too strict (like point 4, below). IMHO, Python's traditional concept of truth is much better than a Pascal-like concept of truth.
2) Should str(True) return "True" or "1": "1" might reduce backwards compatibility problems, but looks strange to me. (repr(True) would always return "True".)
If str and repr were to behave differently from each other, I'd expect repr(True) to return "1" and str(True) to return "True". Interchanging that seems strange. OTOH, I'm worried about backwards compatibility.
3) Should the constants be called 'True' and 'False' (corresponding to None) or 'true' and 'false' (as in C++, Java and C99).
'True' and 'False', please. It makes shadowing of the built-in values by user defined variables much less likely.
4) Should we strive to eliminate non-Boolean operations on bools in the future, through suitable warnings, so that e.g. True+1 would eventually (e.g. in Python 3000 be illegal). Personally, I think we shouldn't; 28+isleap(y) seems totally reasonable to me.
IMHO, `28+isleap(y)` is certainly better than `28+int(isleap(y))`. Used judiciously, booleans in arithmetic expressions can improve readability over conditional statements.
5) Should operator.truth(x) return an int or a bool. Tim Peters believes it should return an int because it's been documented as such. I think it should return a bool; most other standard predicates (e.g. issubtype()) have also been documented as returning 0 or 1, and it's obvious that we want to change those to return a bool.
I'd expect operator.truth to be an alias for 'bool'. If booleans couldn't be used in integer contexts any more, I'd prefer operator.truth to return an int <wink>.
Because of backwards compatibility, the bool type lacks many properties that some would like to see. For example, arithmetic operations with one or two bool arguments is allowed, treating False as 0 and True as 1. Also, a bool may be used as a sequence index.
Counting the number of true values in a collection is a common operation. Using bool as an index is also commonly needed. IMO, allowing booleans in these contexts is a good thing (TM) in general, not only for backwards compatibility.
Other languages (C99, C++, Java) name the constants "false" and "true", in all lowercase. In Python, I prefer to stick with the example set by the existing built-in constants, which all use CapitalizedWords: None, Ellipsis, NotImplemented (as well as all built-in exceptions). Python's built-in module uses all lowercase for functions and types only. But I'm willing to consider the lowercase alternatives if enough people think it looks better.
Please make it consistent with Python, not with an arbitrary set of other languages.
For truth testing of a value, one should use "if", e.g. "if x: print 'Yes'", not comparison to a truth value; "if x == True: print 'Yes'" is not only wrong, it is also strangely redundant.
`if x:` is clear, concise, and generic. Adding comparions or an explicit coercion to bool makes it ugly and more difficult to read. ****** How will bool influence '__nonzero__'? Currently, Python insists that '__nonzero__ should return an int'. Will this be changed to 'should return a bool'? As that would break existing code, I hope not. -- Christian Tanzer tanzer@swing.co.at Glasauergasse 32 Tel: +43 1 876 62 36 A-1130 Vienna, Austria Fax: +43 1 877 66 92
[Christian Tanzer]
... If str and repr were to behave differently from each other, I'd expect repr(True) to return "1" and str(True) to return "True". Interchanging that seems strange.
We try to ensure that eval(repr(x)) reproduce x whenever reasonably possible. That best way to do that here is for repr(True) to return 'True'. We also try to ensure that str(x) be "friendly" whenever possible, even if that means eval(str(x)) has no chance of reproducing x exactly, or even of running without raising an exception. The best way to do that here is also for str(True) to return 'True', but:
OTOH, I'm worried about backwards compatibility.
That's the only reason for str(True) to return '1'. How about str(True) return '1' but str(False) return 'False'? That's what a committee would compromise on <wink>.
... How will bool influence '__nonzero__'? Currently, Python insists that '__nonzero__ should return an int'. Will this be changed to 'should return a bool'?
I hope so.
As that would break existing code, I hope not.
No, "should" != "must". Guido won't call code that returns an int here broken, although he might call it deprecated, and leave you wondering when the hammer will drop <wink>.
Tim Peters <tim.one@comcast.net> writes:
How will bool influence '__nonzero__'? Currently, Python insists that '__nonzero__ should return an int'. Will this be changed to 'should return a bool'?
I hope so.
As that would break existing code, I hope not.
No, "should" != "must". Guido won't call code that returns an int here broken, although he might call it deprecated, and leave you wondering when the hammer will drop <wink>.
Interestingly enough, the exception message says "__nonzero__ should return an int" but really means "__nonzero__ must return an int". I guess under this PEP, the message needs to be changed to "should return a bool", but not the test (or, if you want it more explicit: "__nonzero__ must return an int and should return a bool" :-) Regards, Martin
Interestingly enough, the exception message says "__nonzero__ should return an int" but really means "__nonzero__ must return an int".
You've been staring at standards too much, haven't you? The difference between MUST and SHOULD isn't as clear-cut as most standards people use them. --Guido van Rossum (home page: http://www.python.org/~guido/)
On Sat, Mar 30, 2002, Guido van Rossum wrote:
Interestingly enough, the exception message says "__nonzero__ should return an int" but really means "__nonzero__ must return an int".
You've been staring at standards too much, haven't you? The difference between MUST and SHOULD isn't as clear-cut as most standards people use them.
True, but I'd be happy if Python were to take a step in the formal direction here. I don't think we want to write standards-like documentation, of course, but adopting a bit more precision couldn't hurt and might help. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ Why is this newsgroup different from all other newsgroups?
Aahz writes:
True, but I'd be happy if Python were to take a step in the formal direction here. I don't think we want to write standards-like documentation, of course, but adopting a bit more precision couldn't hurt and might help.
More precision is good if we can maintain accuracy and say what we actually mean. I've been trying to document things that return true or false as returning true or false rather then 1 or 0, but that's not consistently done yet. I think it makes sense to adopt the standard definitions of the requirements terms "should", "must", "may", etc., using the definitions from the IETF RFC (deviation would add confusion, though we should feel free to copy the definition text if we can do so without copyright problems). -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
Guido> You've been staring at standards too much, haven't you? The Guido> difference between MUST and SHOULD isn't as clear-cut as most Guido> standards people use them. In the ISO standards world, ``must'' doesn't mean what you probably think it does: should: encouraged, but not required shall: required must: it is impossible for things to be otherwise So, for example, one can imagine describing a function with a Boolean parameter named x by saying something like: If x is true, the result shall be foo. Otherwise, x must be false, and the result shall be bar. -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
Guido van Rossum writes:
You've been staring at standards too much, haven't you? The difference between MUST and SHOULD isn't as clear-cut as most standards people use them.
Most standards define MUST and SHOULD very specifically, and then use them according to those definitions. There's an IETF RFC specifically for use in writing RFCs, allowing those terms to be defined by reference. A number of W3C specs seems to be using this definition-by-reference as well, with the same RFC providing the actual definitions. I think we'd do well to accept a specific definition of these terms and use them consistently, simply to make our documentation (including docstrings) easier to understand. -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
Guido van Rossum <guido@python.org> writes:
Interestingly enough, the exception message says "__nonzero__ should return an int" but really means "__nonzero__ must return an int".
You've been staring at standards too much, haven't you? The difference between MUST and SHOULD isn't as clear-cut as most standards people use them.
Given Andrew's remark, it really should be "__nonzero__ shall return an in int" :-) Regards, Martin
How will bool influence '__nonzero__'? Currently, Python insists that '__nonzero__ should return an int'. Will this be changed to 'should return a bool'?
I hope so.
Actually, it doesn't matter. This is only called by C code that must then return a *C* 0 or 1, and this code is entirely oblivious to the difference between int and bool. --Guido van Rossum (home page: http://www.python.org/~guido/)
[Tim Peters]
[Christian Tanzer]
... How will bool influence '__nonzero__'? Currently, Python insists that '__nonzero__ should return an int'. Will this be changed to 'should return a bool'?
I hope so.
As that would break existing code, I hope not.
No, "should" != "must". Guido won't call code that returns an int here broken, although he might call it deprecated, and leave you wondering when the hammer will drop <wink>.
What is the likelihood that __nonzero__ could be renamed (at some point in the future) to something a bit more intuitive, like __bool__? I know there would be a lot of user code to change, but assuming one day the hammer will drop and everyone's __nonzero__ will have to change to return a bool, I wouldn't mind seeing the method renamed as well. Or is this a lost cause at this point? --- Patrick K. O'Brien Orbtech
What is the likelihood that __nonzero__ could be renamed (at some point in the future) to something a bit more intuitive, like __bool__? I know there would be a lot of user code to change, but assuming one day the hammer will drop and everyone's __nonzero__ will have to change to return a bool, I wouldn't mind seeing the method renamed as well. Or is this a lost cause at this point?
I think it's a lost cause. It's one of those minor details that might be considered wrong with 20/20 hindsight, but not wrong enough to want to bother fixing it, given the cost (to the user community) of the fix. --Guido van Rossum (home page: http://www.python.org/~guido/)
Tim Peters <tim.one@comcast.net> wrote:
[Christian Tanzer]
... If str and repr were to behave differently from each other, I'd expect repr(True) to return "1" and str(True) to return "True". Interchanging that seems strange.
We try to ensure that eval(repr(x)) reproduce x whenever reasonably possible. That best way to do that here is for repr(True) to return 'True'.
I understood the motivation for `repr(bool(1)) == "True"`. What I wanted to say is that having repr return a readable result and str return a result looking like an internal representation runs counter to the usual conventions used for repr and str.
OTOH, I'm worried about backwards compatibility.
That's the only reason for str(True) to return '1'.
In this case, I'd value consistency between repr/str higher than backwards compatibility.
How about str(True) return '1' but str(False) return 'False'? That's what a committee would compromise on <wink>.
Any self respecting committee would try harder and probably come up with a really symmetric compromise: value str repr True '1' 'True' False 'False' '0' <1.4. wink>
How will bool influence '__nonzero__'? Currently, Python insists that '__nonzero__ should return an int'. Will this be changed to 'should return a bool'?
I hope so.
As that would break existing code, I hope not.
No, "should" != "must".
Fine display of sophisticated hair splitting, but: >>> class Foo: ... def __nonzero__(self): return None ... >>> f = Foo () >>> not f Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: __nonzero__ should return an int >>> I don't care about `should` vs. `must` as long as the new and improved Python won't spit an exception at __nonzero__ code returning an int instead of a bool.
Guido won't call code that returns an int here broken, although he might call it deprecated, and leave you wondering when the hammer will drop <wink>.
Nice tease. But Guido's hammer with regard to future strictification of bool is exactly what makes me uneasy. -- Christian Tanzer tanzer@swing.co.at Glasauergasse 32 Tel: +43 1 876 62 36 A-1130 Vienna, Austria Fax: +43 1 877 66 92
Nice tease. But Guido's hammer with regard to future strictification of bool is exactly what makes me uneasy.
Can you elaborate that? I thought I made it pretty clear I didn't want to strictify bool (though I did open up the question as to whether other people want that). Is that what you call a hammer? --Guido van Rossum (home page: http://www.python.org/~guido/)
Nice tease. But Guido's hammer with regard to future strictification of bool is exactly what makes me uneasy.
Can you elaborate that? I thought I made it pretty clear I didn't want to strictify bool (though I did open up the question as to whether other people want that). Is that what you call a hammer?
You made it clear enough. There are two things making me uneasy though: - A lot of people instantly started complaining about the new bools being not strict enough. Over time, all that clamour might change your POV. - I don't really understand why you proposed this PEP in the first place. For me, Python's (old) way of handling boolean conditions is one of its great strengths. This wasn't always the case. When I started using Python, I was slightly put off by the lack of a boolean data type. But quite quickly I learned to appreciate Python's way of dealing with this issue. All of that was implicit knowledge, though. It took Laura Creighton's well written post to make it explicit for me (thank you, Laura). After reading Alex Martelli's and Laura's arguments, I'm convinced that Python would be better off without this PEP and change to -1. Glad-that-I'm-not-a-language-designer-ly yr's, Christian -- Christian Tanzer tanzer@swing.co.at Glasauergasse 32 Tel: +43 1 876 62 36 A-1130 Vienna, Austria Fax: +43 1 877 66 92
[Christian Tanzer]
For me, Python's (old) way of handling boolean conditions is one of its great strengths. This wasn't always the case. When I started using Python, I was slightly put off by the lack of a boolean data type. But quite quickly I learned to appreciate Python's way of dealing with this issue.
How does the PEP change "Python's (old) way of handling boolean conditions"? Correct me if I'm wrong, but under the PEP, you can still do this: l = range(10) if l: print "My, the list is not empty!" So what's different? // mark
[Fredrik Lundh]
more than one way to do it. more than one concept to explain to newcomers. more than one "obviously correct" way to do things. less is more. etc.
Hmm, I'm not sure I follow. This is Python without the PEP: falseValues = [{}, [], 0, None] for x in falseValues: if not x: print "%s is 'false'." % x This is Python with the PEP: falseValues = [{}, [], 0, None, False] for x in falseValues: if not x: print "%s is 'false'." % x I don't see this vast conceptual chasm between pre and post PEP that you appear to see. Please help me understand. Would the above be clearer if I called the collection of false values 'nothingValues' instead? I, personally, don't think so. // mark
mark wrote:
Hmm, I'm not sure I follow.
This is Python without the PEP:
falseValues = [{}, [], 0, None] for x in falseValues: if not x: print "%s is 'false'." % x
This is Python with the PEP:
falseValues = [{}, [], 0, None, False] for x in falseValues: if not x: print "%s is 'false'." % x
I don't see this vast conceptual chasm between pre and post PEP that you appear to see. Please help me understand.
can you explain how your second example covers everything that's in the PEP? can you explain why the second version is "better", in any sense of that word? </F>
[me]
This is Python with the PEP:
falseValues = [{}, [], 0, None, False] for x in falseValues: if not x: print "%s is 'false'." % x
[Fredrik Lundh]
can you explain how your second example covers everything that's in the PEP?
It doesn't cover everything in the PEP. Here's how I think of the PEP: With this PEP, we get to have our cake and eat it too: 1. There is one obviously correct way to return a truth value. 2. Polymorphous perversity with respect to truth still works. What's everybody complaining about? Cheers, // mark
So what's different?
Fredrik> more than one way to do it. more than one concept to Fredrik> explain to newcomers. more than one "obviously correct" Fredrik> way to do things. less is more. etc. I'm a little confused, because it seems to me that Python already has more than one way to do this particular thing. Specifically: If I want to write a function that answers a yes/no question, I have lots of possible ways of spelling yes (1, 2, "yes", and so on) and lots of possible ways of spelling no (0, {}, None, and so on). There isn't a single preferred way. -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
Andrew Koenig wrote:
Specifically: If I want to write a function that answers a yes/no question, I have lots of possible ways of spelling yes (1, 2, "yes", and so on) and lots of possible ways of spelling no (0, {}, None, and so on). There isn't a single preferred way.
if you study real python code, you'll find that there is. </F>
Specifically: If I want to write a function that answers a yes/no question, I have lots of possible ways of spelling yes (1, 2, "yes", and so on) and lots of possible ways of spelling no (0, {}, None, and so on). There isn't a single preferred way.
Fredrik> if you study real python code, you'll find that there is. I mean that the language doesn't prefer a single way, as opposed to programmers' conventions.
Andrew Koenig wrote:
Specifically: If I want to write a function that answers a yes/no question, I have lots of possible ways of spelling yes (1, 2, "yes", and so on) and lots of possible ways of spelling no (0, {}, None, and so on). There isn't a single preferred way.
Fredrik Lundh wrote:
if you study real python code, you'll find that there is.
Andrew Koenig wrote:
I mean that the language doesn't prefer a single way, as opposed to programmers' conventions.
This doesn't mean anything in practice. Every yes/no function i've encountered returns 1 for yes and 0 for no. (If it returns something else, then it really has more to say than yes or no.) In fact the language actually does prefer a particular way, since x == x returns 1. The addition of another type really does change the set of preferred truth values from {0, 1} to {0, False, 1, True}. The typical practicing Python programmer who wants to say "true" would have to decide -- for each instance -- whether to say "1" or "True". And the programmer cannot happily ignore the issue and stick to using 0 and 1, because if comparisons start to return True and False, dealing with a mix of four truth values is unavoidable. This PEP makes me uneasy because it causes me to see lots of extra casts in Python's future (not to mention confused beginners). -- ?!ng
The addition of another type really does change the set of preferred truth values from {0, 1} to {0, False, 1, True}. The typical practicing Python programmer who wants to say "true" would have to decide -- for each instance -- whether to say "1" or "True".
No. If they want to use the *recommended* values they should always use False or True. 0 and 1 are relegated to the domain of non-representative values of the set of false and true objects, just as None and "true" (the string literal).
And the programmer cannot happily ignore the issue and stick to using 0 and 1, because if comparisons start to return True and False, dealing with a mix of four truth values is unavoidable. This PEP makes me uneasy because it causes me to see lots of extra casts in Python's future (not to mention confused beginners).
You misunderstand it. Strings, lists, numbers and so on are still acceptable as truth values, and when you want to know whether x is true or false, you still have to say "if x:" -- never "if x == True:". In addition, for backward compatibility 0 == False and 1 == True, so mixing them shouldn't be a problem if you know your values are chosen from the set {0, False, 1, True}. If you find yourself comparing an unknown value x to a known truth value to determine whether x is true or false, stop right there: you've *already* got a problem. If you don't have a problem because it works in practice (meaning x is known to be 0 or 1) the backwards compatibility of the PEP kicks in and saves your butt. This won't be deprecated: the PEP promises that False==0 and True==1 will always hold. (There's a question for reviewers about this, but consider it decided -- bools will always mix freely with ints.) --Guido van Rossum (home page: http://www.python.org/~guido/)
On Wed, 3 Apr 2002, Guido van Rossum wrote:
You misunderstand it. Strings, lists, numbers and so on are still acceptable as truth values, and when you want to know whether x is true or false, you still have to say "if x:" -- never "if x == True:".
Yes yes. I understand this part just fine. It's not the *listening* i'm concerned about -- 'if' takes care of that easily for me. It hears [], None, 0 as false and hears 'spam', {1: 2}, 47.3 as true. It's the *speaking*. When i want to say true or false, then there's the dilemma. I know, your answer is "you should always just say True or False", and Mark McEahern said the same thing. But this cannot be so in practice: *everything* already returns 0 or 1. (It may be possible to get around this if we commit to changing the entire standard library before releasing a version of Python with True and False, but alas, this is not the only issue... read on.) As long as True and False are somewhere represented as 0 and 1, the values 0 and 1 will never lose legitimacy as booleans. This business with str() and/or repr() producing "0" or "1" for backwards compatibility prevents us from considering 0 and 1 relegated to a truly non-boolean status. Consider this: >>> a = [0, False, 1, True] >>> print a [0, 0, 1, 1] >>> for x in a: print x 0 0 1 1 Good heavens! What about this: >>> d = {} >>> d[0] = 'zero' >>> d[False] = 'false' >>> len(d) 1 or 2? Basically, the concept of having a permanently schizophrenic type in the language scares me. The above shows, i believe, that a reasonable implementation must print True as True and False as False, and never mention 1 or 0. Moreover, as soon as you start sorting a bag of objects, or keying dictionaries on objects, you are forced to run into the distinction between 0 and False, and between 1 and True. I'm not against the idea of booleans, of course -- but i do think that halfway booleans are worse than what we have now. And getting to real booleans [*] involves real pain; it's just a question of whether that pain is worth it. Even if we get all the way there -- as in we manage to convert enough code and convince everyone to use the new style -- i will never ever want "and" and "or" to return booleans (i just hope that doesn't confuse anyone). -- ?!ng [*] By real booleans, i mean the following. (Booleans would have to behave like this for me to consider them "good enough" to be better than what we have now.) >>> False, repr(False), str(False) (False, 'False', 'False') >>> True, repr(False), str(False) (True, 'False', 'False') >>> False + True TypeError... >>> False == None 0 >>> False == 0 0 >>> True == 1 0 >>> {0: 0, False: False, 1: 1, True: True} {0: 0, False: False, 1: 1, True: True} ... and probably >>> None < False < True < 0 True (Hee hee -- i suppose the fact that "boolean" starts with a "b" gets us this for free. But i wonder how many people are going to be puzzled by True < 0?)
[Ka-Ping Yee]
Consider this:
>>> a = [0, False, 1, True]
>>> print a [0, 0, 1, 1]
>>> for x in a: print x 0 0 1 1
Actually, if you use the sample code provided by the PEP and run this you get:
a = [0, False, 1, True] print a [0, False, 1, True]
for x in a: print x 0 False 1 True
It's the *speaking*. When i want to say true or false, then there's the dilemma.
I know, your answer is "you should always just say True or False", and Mark McEahern said the same thing. But this cannot be so in practice: *everything* already returns 0 or 1. (It may be possible to get around this if we commit to changing the entire standard library before releasing a version of Python with True and False, but alas, this is not the only issue... read on.)
As long as True and False are somewhere represented as 0 and 1, the values 0 and 1 will never lose legitimacy as booleans. This business with str() and/or repr() producing "0" or "1" for backwards compatibility prevents us from considering 0 and 1 relegated to a truly non-boolean status.
But that's a variant of the PEP that no-one except Marc-Andre has spoken in favor of. The PEP proposes str(True) == "True".
Consider this:
>>> a = [0, False, 1, True]
>>> print a [0, 0, 1, 1]
>>> for x in a: print x 0 0 1 1
Good heavens!
It will print 0 False 1 True
What about this:
>>> d = {} >>> d[0] = 'zero' >>> d[False] = 'false' >>> len(d) 1 or 2?
What about this: >>> d = {} >>> d[0] = 'int' >>> d[0.0] = 'float' >>> d[0j] = 'complex' >>> print len(d) 1 or 3? False and True are numbers, and they are equal (==) to 0 and 1; everything else follows from there.
Basically, the concept of having a permanently schizophrenic type in the language scares me. The above shows, i believe, that a reasonable implementation must print True as True and False as False, and never mention 1 or 0.
And this is what the PEP proposes (despite its brief mention of an alternative).
Moreover, as soon as you start sorting a bag of objects, or keying dictionaries on objects, you are forced to run into the distinction between 0 and False, and between 1 and True.
No you're not. d[0] and d[False] retrieve the same value, as do d[0L], d[0.0], and d[0j].
I'm not against the idea of booleans, of course -- but i do think that halfway booleans are worse than what we have now. And getting to real booleans [*] involves real pain; it's just a question of whether that pain is worth it. Even if we get all the way there -- as in we manage to convert enough code and convince everyone to use the new style -- i will never ever want "and" and "or" to return booleans (i just hope that doesn't confuse anyone).
-- ?!ng
[*] By real booleans, i mean the following. (Booleans would have to behave like this for me to consider them "good enough" to be better than what we have now.)
>>> False, repr(False), str(False) (False, 'False', 'False') >>> True, repr(False), str(False) (True, 'False', 'False') >>> False + True TypeError...
That's just one textbook idea of what a Boolean "should" be.
>>> False == None 0 >>> False == 0 0 >>> True == 1 0 >>> {0: 0, False: False, 1: 1, True: True} {0: 0, False: False, 1: 1, True: True}
... and probably
>>> None < False < True < 0 True
(Hee hee -- i suppose the fact that "boolean" starts with a "b" gets us this for free. But i wonder how many people are going to be puzzled by True < 0?)
Yuck. --Guido van Rossum (home page: http://www.python.org/~guido/)
From: "Guido van Rossum" <guido@python.org>
False and True are numbers, and they are equal (==) to 0 and 1; everything else follows from there.
FWIW, that's the way it is in C++, and almost all of the same conclusions fall out of it. Also, as distasteful as it may seem intellectually, I have yet to hear any complaints about this situation in practice... so it seems to work out OK. I also note that this is not something people seem to have trouble with in making the transition from C to C++, which is analogous to the problem of legacy 1/0 code. -Dave
David> FWIW, that's the way it is in C++, and almost all of the same David> conclusions fall out of it. Also, as distasteful as it may seem David> intellectually, I have yet to hear any complaints about this David> situation in practice... so it seems to work out OK. It seems only fair that I should mention a complaint: The fact that bool is an integral type makes it necessary to have operator const void* rather than operator bool as members of istream and ostream for end-of-file testing. Otherwise, writing ``cin << x'' instead of ``cin >> x'' by mistake would result in converting cin to bool and thence to int, shifting the result right by x bits, and throwing it away. -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
Guido van Rossum wrote:
False and True are numbers [...]
That's exactly where this falls down. Patrick O'Brien wrote:
[...] True and False could be dictionary keys separate from 0 and 1.
Guido van Rossum wrote:
No. That would break backwards compatibility. False==0, and True==1; everything else follows from that. (But False is not 0, and True is not 1!)
This is a strange distinction to make -- one that is not made in any other programming language i have ever encountered. Of course *i* get it and *you* get it -- but it will be so hard to teach this particular weirdness that i consider it fatal to the proposal. -- ?!ng
----- Original Message ----- From: "Ka-Ping Yee" <ping@lfw.org>
Guido van Rossum wrote:
No. That would break backwards compatibility. False==0, and True==1; everything else follows from that. (But False is not 0, and True is not 1!)
This is a strange distinction to make -- one that is not made in any other programming language i have ever encountered. Of course *i* get it and *you* get it -- but it will be so hard to teach this particular weirdness that i consider it fatal to the proposal.
C++ is /exactly/ like that. I think also C99, but don't quote me.
Guido van Rossum wrote:
False and True are numbers [...]
That's exactly where this falls down.
Patrick O'Brien wrote:
[...] True and False could be dictionary keys separate from 0 and 1.
Guido van Rossum wrote:
No. That would break backwards compatibility. False==0, and True==1; everything else follows from that. (But False is not 0, and True is not 1!)
Ka-Ping Yee wrote: This is a strange distinction to make -- one that is not made in any other programming language i have ever encountered. Of course *i* get it and *you* get it -- but it will be so hard to teach this particular weirdness that i consider it fatal to the proposal.
This strange distinction seems to be the crux of the controversy surrounding this PEP. I thought it a bit strange at first too. But the more I think about it the less it troubles me. In fact, it is starting to feel downright Pythonic. True and False are singleton instances of a Boolean subclass of the Integer class. As a subclass, much of, but not all of, their behavior is inherited from Integer. So False often acts like 0 and True often acts like 1. But not always. Because they are instances of a subclass. That's what subclassing allows. This is Object Orientation 101. I think the concepts truly are no less difficult to understand or explain than plenty of other aspects of the Python language. The way Boolean is being implemented may not be what one would expect, and it may not be what other languages do, and it may take a little getting used to, and it may upset some people's preconceptions of truth values, but I think if everyone steps back and looks at this objectively they have to admit that this "strange distinction" really isn't too terribly strange after all. There are plenty of Python features that still make my head hurt. This new Boolean isn't one of them. --- Patrick K. O'Brien Orbtech
No. That would break backwards compatibility. False==0, and True==1; everything else follows from that. (But False is not 0, and True is not 1!)
Ping> This is a strange distinction to make -- one that is not made in Ping> any other programming language i have ever encountered. I would be very surprised if none of the Lisp family of languages has similar anomalies in the relationship between eq and equal. -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
[Ka-Ping Yee]
The addition of another type really does change the set of preferred truth values from {0, 1} to {0, False, 1, True}. The typical practicing Python programmer who wants to say "true" would have to decide -- for each instance -- whether to say "1" or "True".
Nope, the set of preferred truth values would be: True, False You could, of course, continue to use 1, 0 if you like. In the long run, why would you?
And the programmer cannot happily ignore the issue and stick to using 0 and 1 [...]
Of course she can. In the long run, why would she?
[...] because if comparisons start to return True and False, dealing with a mix of four truth values is unavoidable. This PEP makes me uneasy because it causes me to see lots of extra casts in Python's future (not to mention confused beginners).
Can you give me just one example of these supposedly necessary 'extra casts' that is not gratuitous and intentionally sloppy? I'm too dense to come up with one on my own. Hmm, this PEP makes me delighted because I see lots of clean code and happy, productive beginners. But, to each his own. Cheers, // mark
"KY" == Ka-Ping Yee <ping@lfw.org> writes:
KY> This doesn't mean anything in practice. Every yes/no function KY> i've encountered returns 1 for yes and 0 for no. Now we see a way out. Rename the constants to Yes and No and add a builtin alias for bool() calld theAnswerTo(). Thus all our conditionals would be written like so: if theAnswerTo(myQuestion) is Yes: # do it truthful-ly y'rs, -Barry
andrew wrote:
Fredrik> if you study real python code, you'll find that there is.
I mean that the language doesn't prefer a single way, as opposed to programmers' conventions.
it's more than just a convention -- built-in functions and operators always return 0 for false and 1 for true whenever they need to in- vent a boolean value. this is documented in the language reference. (and if you look inside, there's something called Py_False that's an integer zero, and something called Py_True that is an integer one). this is also true for the standard library, and lots of major extensions. also note that the PEP says "The bool type would be a straightforward subtype (in C) of the int type, and the values False and True would behave like 0 and 1 in most respects". Guido didn't pick those values out of thin air... </F>
I mean that the language doesn't prefer a single way, as opposed to programmers' conventions.
Fredrik> it's more than just a convention -- built-in functions and Fredrik> operators always return 0 for false and 1 for true whenever Fredrik> they need to invent a boolean value. this is documented in Fredrik> the language reference. On the other hand, the library isn't nearly so consistent. Just for fun, I started browsing the library documentation at random. The very first page I visited was the description of gc, the garbage collector interface. The first function that returns a truth value there is described as isenabled() Returns true if automatic collection is enabled. Some more examples: 2.2.6.1 String Methods endswith(suffix[,start[,end]]) Return true if the string ends with the specified suffix, otherwise return false. Similarly for isalnum, isalpha, isdigit, islower, isspace. 2.2.8 File Objects: isatty() Return true if the file is connected to a tty(-like) device, else false. 6.2 os.path -- Common pathname manipulations exists(path) Return true if path refers to an existing path. I'm sure there are others. -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
Andrew Koenig wrote:
On the other hand, the library isn't nearly so consistent. Just for fun, I started browsing the library documentation at random.
The very first page I visited was the description of gc, the garbage collector interface. The first function that returns a truth value there is described as
isenabled() Returns true if automatic collection is enabled.
import gc gc.isenabled() 1 type(gc.isenabled()) <type 'int'>
endswith(suffix[,start[,end]]) Return true if the string ends with the specified suffix, otherwise return false.
"spam".endswith("m") 1 type("spam".endswith("m")) <type 'int'>
isatty() Return true if the file is connected to a tty(-like) device, else false.
sys.stdout.isatty() 1 type(sys.stdout.isatty()) <type 'int'>
exists(path) Return true if path refers to an existing path.
os.path.exists("/etc/passwd") 1 type(os.path.exists("/etc/passwd")) <type 'int'>
looks pretty consistent to me ;-) (Python docs tend to use "true" to mean anything which isn't "false", and where operator.truth returns the right thing. this won't change post-PEP -- if you start interpreting "return true" as "return True", you'll be asking for trouble) </F>
Fredrik Lundh writes:
(Python docs tend to use "true" to mean anything which isn't "false", and where operator.truth returns the right thing. this won't change post-PEP -- if you start interpreting "return true" as "return True", you'll be asking for trouble)
That's right, but see my other note: Guido would like things like this to say "return True", which is different. -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
(Python docs tend to use "true" to mean anything which isn't "false", and where operator.truth returns the right thing. this won't change post-PEP -- if you start interpreting "return true" as "return True", you'll be asking for trouble)
Unclear. I intend to change as many functions as I reasonably can to return True/False, and I intend to change their documentation to match. While some of the documentation indeed talks of "true" and "false", those are not well-defined values. They are properties of values, and I find it bad style to say "f() returns true if X and false otherwise". I would have written that either as "f() returns a true value if X and a false value otherwise" (in case I don't want to commit to returning 0 or 1), or "f() returns 1 if X, 0 otherwise" (in case I do). --Guido van Rossum (home page: http://www.python.org/~guido/)
Fredrik> looks pretty consistent to me ;-) It may look that way, but if the documentation is to be believed, it's just happenstance. Fredrik> (Python docs tend to use "true" to mean anything which isn't Fredrik> "false", and where operator.truth returns the right thing. Fredrik> this won't change post-PEP -- if you start interpreting Fredrik> "return true" as "return True", you'll be asking for trouble) Indeed. In other words, the library does not have a consistent convention for what values to return for answers to yes/no questions. A particular implementation may happen to follow such a convention, but it is under no obligation to do so. -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
Andrew Koenig writes:
The very first page I visited was the description of gc, the garbage collector interface. The first function that returns a truth value there is described as
isenabled() Returns true if automatic collection is enabled.
I spent a bit of time talking to Guido about the effect of PEP 285 on the documentation. My own preference is to do the same as the examples you cite: use the English words "true" and "false", and not give concrete typed values. Guido would rather *not* have it this way, but rather document the exact concrete return values. He'd have me refer to "True" and "False" in the same way we refer to "None", and refer to the return type as "bool" (if indeed we're using the new bool type), or, if 1 and 0 are the return values, document them specifically as well. For function arguments, he'd like to see truth values described as Boolean, and the values would be the English "true" and "false". Translations would be able to translate the English words, but not the True and False values, which are language constants (as constant as built-ins are). Guido & I may clash over this a few times. ;-( -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
On Wed, 3 Apr 2002, Fredrik Lundh wrote:
Andrew Koenig wrote:
Specifically: If I want to write a function that answers a yes/no question, I have lots of possible ways of spelling yes (1, 2, "yes", and so on) and lots of possible ways of spelling no (0, {}, None, and so on). There isn't a single preferred way.
if you study real python code, you'll find that there is.
Having managed 200k+ LOC Python projects with at times 20+ developers, I find that there isn't. At least not without getting out a baseball bat. The first thing many of our programmers want to do at the top of their modules is to define: False = 0 True = not False or false = 0 true = 1 or TRUE = 1 FALSE = 0 or from my_local_trashcan_module import true, false or some fairly extreme object implementations in Python or C. Of course, I have to break their fingers and force them to stop re-inventing their own true and false literals, and to use 1's and 0's like the rest of the disadvantaged Python world. Ah, the joys of code review.... Believe it or not, this, naming conventions, and consistent exception handling styles are among the biggest bones of contention we have during our code review sessions. The moral of the story is that I will be extremely happy once there is a globally blessed way of spelling true and false other than 1 and 0. Of course, I and my team aren't exactly typical Python developers. Though wouldn't it be nice if we were? -Kevin -- Kevin Jacobs The OPAL Group - Enterprise Systems Architect Voice: (216) 986-0710 x 19 E-mail: jacobs@theopalgroup.com Fax: (216) 986-0714 WWW: http://www.theopalgroup.com
[Kevin Jacobs]
The moral of the story is that I will be extremely happy once there is a globally blessed way of spelling true and false other than 1 and 0. Of course, I and my team aren't exactly typical Python developers. Though wouldn't it be nice if we were?
I'd like to consciously go out of my way and violate all rules of netiquette and say simply this: Me too. Cheers, // mark
FWIW, I do the same thing every time I need to work with booleans. I try to "say what I mean" when I program, and, I'm sorry, but "return 1|0" just doesn't say "I mean to return something which will be interpreted as true|false". I want the language to help me say what I mean. I do understand about the backward-compatibility issues introduced by adding bool, but I don't understand why there are claims that there will be more than one official way to return boolean results. It seems to me that the only thing pointing to 1|0 as the right way would be legacy code, and everything in the language would point at true|false. -Dave ----- Original Message ----- From: "Mark McEahern" <marklists@mceahern.com>
[Kevin Jacobs]
The moral of the story is that I will be extremely happy once there is a globally blessed way of spelling true and false other than 1 and 0. Of course, I and my team aren't exactly typical Python developers. Though wouldn't it be nice if we were?
I'd like to consciously go out of my way and violate all rules of netiquette and say simply this:
Me too.
"AK" == Andrew Koenig <ark@research.att.com> writes:
AK> Specifically: If I want to write a function that answers a AK> yes/no question, I have lots of possible ways of spelling yes AK> (1, 2, "yes", and so on) and lots of possible ways of spelling AK> no (0, {}, None, and so on). There isn't a single preferred AK> way. This is a strong pro (IMO) for PEP 285. When I code a flag, do I use 0 and 1, or None and 1, or ...what? When you see code that says "x.isthing = 1" what does that tell you about how this attribute will be used? I like the bool because it provides clarity when coding flag values, and you still maintain the (IMHO beautiful) feature of being able to say "this variable has a value, or it's never been set". I.e. flag=None, flag=True, flag=False. The strongest con for PEP 285 seems to be the natural inclination for people to want to write "if cond == True:". Heck, /I/ certainly wrote that the first time I thought about bools years ago (and even made that "work" in my early pure-Python prototypes). But it doesn't make sense since "if cond:" and "if not cond:" are better than comparison against the boolean constants. There's two reasons we teased out about why people may tend to want to compare against True and False. People may think that the comparison is somehow part of the syntax of the if statement. Also, using the comparison can sometimes be more aligned with English grammar. E.g. this reads well on its own: if year.isLeapYear(): Note the use of `is' which tends to be a common prefix on predicates. But if you don't use that prefix, then you are maybe inclined to add the test: if flag == True: so that it reads somewhat like "if flag is True". (Why don't we tend to write exactly that? Maybe because as Python programmers, we're trained not to use identity tests except in very limited situations?). -Barry
[Barry A. Warsaw]
The strongest con for PEP 285 seems to be the natural inclination for people to want to write "if cond == True:".
I don't see why you want to blame that inclination on PEP 285. Folks are perfectly capable of monstrosities like this without it: x = isinstance(foo, bar) if x == 1: ... If, as Fredrik says, there's already one obvious way to return True (and that's the int 1), then we already have the problem that people will be inclined to compare something to this canonical truth in conditional statements. I suppose you're saying that PEP 285 makes it worse? I have no idea whether you're right. It certainly doesn't make ME more likely to do it. ;-) Cheers, // mark
"Guido van Rossum" <guido@python.org> writ:
Dear reviewers:
I'm particularly interested in hearing your opinion about the following three issues:
1) Should this PEP be accepted at all.
Depends on the resolution of #2 and #3 ;-)
2) Should str(True) return "True" or "1": "1" might reduce backwards compatibility problems, but looks strange to me. (repr(True) would always return "True".)
str(True) must return 'True' or there isn't much point in bothering with this, IMO.
3) Should the constants be called 'True' and 'False' (corresponding to None) or 'true' and 'false' (as in C++, Java and C99).
As a language interoperability guy, I prefer to keep as much precise correspondence as possible with C++ (and Java), so I vote for 'true'/'false'.
Most other details of the proposal are pretty much forced by the backwards compatibility requirement; e.g. True == 1 and True+1 == 2 must hold, else reams of existing code would break.
Guess what other language made that same choice for the same reasons?
Minor additional issues:
4) Should we strive to eliminate non-Boolean operations on bools in the future, through suitable warnings, so that e.g. True+1 would eventually (e.g. in Python 3000 be illegal). Personally, I think we shouldn't; 28+isleap(y) seems totally reasonable to me.
Changing my position somewhat from earlier, I'll vote in my project's self-interest: I agree with your inclination to allow bool to be "promoted" to an int in these conditions.
5) Should operator.truth(x) return an int or a bool. Tim Peters believes it should return an int because it's been documented as such. I think it should return a bool; most other standard predicates (e.g. issubtype()) have also been documented as returning 0 or 1, and it's obvious that we want to change those to return a bool.
I agree again! 6) Should we eventually remove the inheritance relationship between Int and Bool? I hope so. Bool is-a Int doesn't seem like the right relationship to me, unless perhaps we make Int is-a Long... naah, not even then. -Dave
[David Abrahams]
6) Should we eventually remove the inheritance relationship between Int and Bool?
I hope so. Bool is-a Int doesn't seem like the right relationship to me, unless perhaps we make Int is-a Long... naah, not even then.
Hm. In your favorite language bool is one of the integral types. Doesn't that imply pretty much the same thing? Anyway, do you have a use case where it matters? --Guido van Rossum (home page: http://www.python.org/~guido/)
----- Original Message ----- From: "Guido van Rossum" <guido@python.org> To: "David Abrahams" <david.abrahams@rcn.com> Cc: <python-list@python.org>; <python-dev@python.org> Sent: Saturday, March 30, 2002 8:47 AM Subject: Re: [Python-Dev] PEP 285: Adding a bool type
[David Abrahams]
6) Should we eventually remove the inheritance relationship between Int and Bool?
I hope so. Bool is-a Int doesn't seem like the right relationship to me, unless perhaps we make Int is-a Long... naah, not even then.
Hm. In your favorite language bool is one of the integral types. Doesn't that imply pretty much the same thing?
Well, I try not to play favorites <0.002 wink>, but the implicit conversion rules in C++ are admittedly and unfortunately liberal. However, bool is detectably NOT derived from int in C++: void f(int const&); f(false); // error! assert(boost::is_base_and_derived<int,bool>::value); // fails!
Anyway, do you have a use case where it matters?
Given that Bool is immutable, I have no cases other than examples of type introspection which can be written to account for the fact that Bool is-a Int. However, it does make certain things trickier to get right: numeric_types = [ Int, Long, Bool, Float, Complex ] for t in numeric_types: if isinstance(x, t): # Do something... This sort of thing could depend on getting the order of the list right (I didn't). -Dave
[David Abrahams]
6) Should we eventually remove the inheritance relationship between Int and Bool?
I hope so. Bool is-a Int doesn't seem like the right relationship to me, unless perhaps we make Int is-a Long... naah, not even then.
Hm. In your favorite language bool is one of the integral types. Doesn't that imply pretty much the same thing?
Well, I try not to play favorites <0.002 wink>, but the implicit conversion rules in C++ are admittedly and unfortunately liberal. However, bool is detectably NOT derived from int in C++:
void f(int const&); f(false); // error! assert(boost::is_base_and_derived<int,bool>::value); // fails!
I don't understand this example, but I though int and bool weren't considered classes in C++, so I'm not sure this matters. I know you don't care, but C99 *does* consider bool an integral type. Another argument for deriving bool from int: implementation inheritance. A bool must behave just like an int, and this is most easily accomplished this way: the bool type doesn't have implementations for most operations: it inherits them from the int type, which find a bool acceptable where an int is required.
Anyway, do you have a use case where it matters?
Given that Bool is immutable, I have no cases other than examples of type introspection which can be written to account for the fact that Bool is-a Int. However, it does make certain things trickier to get right:
numeric_types = [ Int, Long, Bool, Float, Complex ] for t in numeric_types: if isinstance(x, t): # Do something...
This sort of thing could depend on getting the order of the list right (I didn't).
Using isinstance() this way is usually bad. And who knows that long doesn't inherit from int? (At some point in the future it may, or they may become the same thing -- see PEP 237. --Guido van Rossum (home page: http://www.python.org/~guido/)
[We had a discussion about this a couple of months ago on c.l.py, which Guido probably didn't see, so I wanted to briefly raise this issue here.] On Sat, Mar 30, 2002, Guido van Rossum wrote:
David Abrahams:
Given that Bool is immutable, I have no cases other than examples of type introspection which can be written to account for the fact that Bool is-a Int. However, it does make certain things trickier to get right:
numeric_types = [ Int, Long, Bool, Float, Complex ] for t in numeric_types: if isinstance(x, t): # Do something...
This sort of thing could depend on getting the order of the list right (I didn't).
Using isinstance() this way is usually bad. And who knows that long doesn't inherit from int? (At some point in the future it may, or they may become the same thing -- see PEP 237.
The problem is that in some cases the __init__ for a class needs to dispatch based on the type of the operand. For example, int() takes both numbers and strings. What should we recommend as "standard practice" for this issue if isinstance() is disrecommended? -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ Why is this newsgroup different from all other newsgroups?
From: Aahz <aahz@pythoncraft.com>
The problem is that in some cases the __init__ for a class needs to dispatch based on the type of the operand. For example, int() takes both numbers and strings. What should we recommend as "standard practice" for this issue if isinstance() is disrecommended?
PEP 246 (?) <wink> PS: I had a very heated discussion with Alex Martelli on anygui-devel whether the PEP is ready for prime time, and whether wrapping is so generally harmless, but I think at some point the PEP should nevertheless be seriously discussed. Likely the Zope3 people have some real data/experience on adaptation to share. PPS: it should be noted that the PEP does *not* require first class interfaces to be adopted. Those are an orthogonal issue.
On Sat, Mar 30, 2002, Guido van Rossum wrote:
David Abrahams:
Given that Bool is immutable, I have no cases other than examples of type introspection which can be written to account for the fact that Bool is-a Int. However, it does make certain things trickier to get right:
numeric_types = [ Int, Long, Bool, Float, Complex ] for t in numeric_types: if isinstance(x, t): # Do something...
This sort of thing could depend on getting the order of the list right (I didn't).
Using isinstance() this way is usually bad. And who knows that long doesn't inherit from int? (At some point in the future it may, or they may become the same thing -- see PEP 237.
The problem is that in some cases the __init__ for a class needs to dispatch based on the type of the operand. For example, int() takes both numbers and strings. What should we recommend as "standard practice" for this issue if isinstance() is disrecommended?
Ah, bollocks. David was just being annoying, and I was being annoying back. You can use isinstance() when it makes sense, like your example. --Guido van Rossum (home page: http://www.python.org/~guido/)
----- Original Message ----- From: "Guido van Rossum" <guido@python.org> To: "David Abrahams" <david.abrahams@rcn.com> Cc: <python-list@python.org>; <python-dev@python.org> Sent: Saturday, March 30, 2002 9:23 AM Subject: Re: [Python-Dev] PEP 285: Adding a bool type
[David Abrahams]
6) Should we eventually remove the inheritance
relationship
between Int and Bool?
I hope so. Bool is-a Int doesn't seem like the right
relationship to
me, unless perhaps we make Int is-a Long... naah, not even then.
Hm. In your favorite language bool is one of the integral types. Doesn't that imply pretty much the same thing?
Well, I try not to play favorites <0.002 wink>, but the implicit conversion rules in C++ are admittedly and unfortunately liberal. However, bool is detectably NOT derived from int in C++:
void f(int const&); f(false); // error! assert(boost::is_base_and_derived<int,bool>::value); // fails!
I don't understand this example, but I though int and bool weren't considered classes in C++, so I'm not sure this matters.
That's correct, they're not. Still, it matters that when you do type introspection the system doesn't tell you they're related by inheritance. I'm sure I've written programs that would break if that answer suddenly changed. My point is just that "int and bool are integral types" is a very different thing from "bool is derived from int" (for another example, derivation implies one-way implicit conversion from derived->base, whereas the numeric types are all implicitly inter-convertible).
I know you don't care, but C99 *does* consider bool an integral type.
As does C++, and why wouldn't I care?
Another argument for deriving bool from int: implementation inheritance. A bool must behave just like an int, and this is most easily accomplished this way: the bool type doesn't have implementations for most operations: it inherits them from the int type, which find a bool acceptable where an int is required.
I recognize that as the /only/ real argument for such derivation. Many are suspicious of public implementation inheritance these days, tending to prefer delegation so as not to expose a false is-a relationship, breaking the LSP. However, I think bool may squeeze by on that count since as I said it's immutable. Uh, whoops, no: the repr() and str() functions, for example change the way it works in incompatible ways.
Anyway, do you have a use case where it matters?
Given that Bool is immutable, I have no cases other than examples of type introspection which can be written to account for the fact that Bool is-a Int. However, it does make certain things trickier to get right:
numeric_types = [ Int, Long, Bool, Float, Complex ] for t in numeric_types: if isinstance(x, t): # Do something...
This sort of thing could depend on getting the order of the list right (I didn't).
Using isinstance() this way is usually bad.
Can you say something about this that doesn't invoke a moral judgement? I don't believe in evil programming practices, just useful/fragile ones.
And who knows that long doesn't inherit from int?
In Python, people make assumptions based on their implementation because for details like this there is often no other guideline.
(At some point in the future it may, or they may become the same thing -- see PEP 237.
Well, sure, anything could happen. Anyway, I don't have a strong position about this issue, but it rubs my instincts the wrong way. happy-as-a-wet-cat-ly y'rs, dave
Guido> Another argument for deriving bool from int: implementation Guido> inheritance. A bool must behave just like an int, and this is Guido> most easily accomplished this way: the bool type doesn't have Guido> implementations for most operations: it inherits them from the Guido> int type, which find a bool acceptable where an int is Guido> required. Liskov substitutibility would seem to suggest deriving int from bool, not the other way around. That is: If I have a program that uses bool values, I can change it so that it uses int values without affecting the behavior of the program. The reverse is not true. I wonder if this is the circle-ellipse problem over again? -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
----- Original Message ----- From: "Andrew Koenig" <ark@research.att.com>
Liskov substitutibility would seem to suggest deriving int from bool, not the other way around. That is: If I have a program that uses bool values, I can change it so that it uses int values without affecting the behavior of the program. The reverse is not true.
Even that won't work, because of the different repr/str behavior... one reason I don't like the inheritance relationship.
I wonder if this is the circle-ellipse problem over again?
square-rectangle-ly y'rs, dave
David> Even that won't work, because of the different repr/str behavior... one David> reason I don't like the inheritance relationship. Ah -- hadn't thought of that.
Guido> Another argument for deriving bool from int: implementation Guido> inheritance. A bool must behave just like an int, and this is Guido> most easily accomplished this way: the bool type doesn't have Guido> implementations for most operations: it inherits them from the Guido> int type, which find a bool acceptable where an int is Guido> required.
[ARK]
Liskov substitutibility would seem to suggest deriving int from bool, not the other way around. That is: If I have a program that uses bool values, I can change it so that it uses int values without affecting the behavior of the program. The reverse is not true.
I wonder if this is the circle-ellipse problem over again?
Probably. We're inheriting implemetation. --Guido van Rossum (home page: http://www.python.org/~guido/)
David> void f(int const&); David> f(false); // error! For the sake of nitpicking correctness, I cannot help but point out that there is nothing wrong with the call of f(false) above unless you drop the "const" from f's parameter type. -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
----- Original Message ----- From: "Andrew Koenig" <ark@research.att.com>
David> void f(int const&); David> f(false); // error!
For the sake of nitpicking correctness, I cannot help but point out that there is nothing wrong with the call of f(false) above unless you drop the "const" from f's parameter type.
Yeah, I realized that after posting it, and hoped nobody would notice. void f(int const*); bool x = false; f(&x); // error! thanks-for-nothing-ly y'rs, dave
6) Should we eventually remove the inheritance relationship between Int and Bool?
I hope so. Bool is-a Int doesn't seem like the right relationship to me, unless perhaps we make Int is-a Long... naah, not even then.
Guido> Hm. In your favorite language bool is one of the integral types. Guido> Doesn't that imply pretty much the same thing? What it really implies is that arithmetic operations on bool values are permitted and promote to int. -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
[Guido van Rossum]
I offer the following PEP for review by the community. If it receives a favorable response, it will be implemented in Python 2.3.
Yikes! I've read every single response to this so far. It has been quite enlightening for me. At the same time, I hope it ends soon. ;-) At this point I'm +1 on the proposal, but I have a favor to ask. While a lot of the issues raised have been revealing and provocative, I think the vast majority are tangential to the real substance of this PEP. I'm having trouble separating what is important from what is not. Is there any way we can focus the issue a bit? Specifically: It feels like there are too many combinations available in the PEP. (Or maybe too many questions and options which could result in weird combinations.) I'd like to see a new version with Guido's latest thoughts. For example, are we closer to a resolution on the str() and repr() issues? The only behavior I like is that exhibited by the sample code:
str(True) 'True' repr(True) 'True'
How about the case of the constants: true, false vs. True, False? I like the latter, which match None. They would also stand out in the new docstrings mentioned by Fred. How about operator.truth()? Has Tim changed his mind at all? Has Guido? If this PEP were implemented and "all built-in operations that conceptually return a Boolean" were updated, what verifiable examples of code breakage would we have? The issue of dictionary keys has been raised, and is a bit perplexing, but not overly so, IMO. However, I'd like confirmation that this is indeed the intended behavior:
d = {} d[0] = 'First' d {0: 'First'} d[False] = 'Second' d {0: 'Second'} d[0.0] = 'Third' d {0: 'Third'} d[()] = 'Fourth' d {0: 'Third', (): 'Fourth'} d[None] = 'Fifth' d {0: 'Third', (): 'Fourth', None: 'Fifth'}
Or are there any situations like this where you would want True and False to be a bit more like None and a bit less like ints? Meaning True and False could be dictionary keys separate from 0 and 1. Other than that, are there any concrete issues that have been raised that need to be addressed by the PEP? I understand the uneasy feeling felt by many as I felt it myself. However, I've read all the newsgroup postings and reread the PEP and I don't feel very queasy any more. I think this PEP is a step in the right direction. I would hope that clarifying some of the genuine issues might help to bring about consensus and reduce some of the rhetoric. --- Patrick K. O'Brien Orbtech
The issue of dictionary keys has been raised, and is a bit perplexing, but not overly so, IMO. However, I'd like confirmation that this is indeed the intended behavior:
d = {} d[0] = 'First' d {0: 'First'} d[False] = 'Second' d {0: 'Second'} d[0.0] = 'Third' d {0: 'Third'} d[()] = 'Fourth' d {0: 'Third', (): 'Fourth'} d[None] = 'Fifth' d {0: 'Third', (): 'Fourth', None: 'Fifth'}
Or are there any situations like this where you would want True and False to be a bit more like None and a bit less like ints? Meaning True and False could be dictionary keys separate from 0 and 1.
I see that you just addressed this and confirmed that True==1 and False==0 in all contexts. That's fine by me. I just wanted confirmation. --- Patrick K. O'Brien Orbtech
It feels like there are too many combinations available in the PEP. (Or maybe too many questions and options which could result in weird combinations.) I'd like to see a new version with Guido's latest thoughts.
My thoughts haven't changed at all. :-) The first round of discussion (on python-dev) brought up some contentious issues where people disagreed strongly with particular details proposed in the PEP, and I put those contentious issues at the top of the review list. If you go to the latest version of the PEP on the web (http://www.python.org/peps/pep-0285.html) you'll see that I've added some other issues that were brought up in the more recent discussion.
For example, are we closer to a resolution on the str() and repr() issues? The only behavior I like is that exhibited by the sample code:
str(True) 'True' repr(True) 'True'
So far I believe only Marc-Andre has expressed himself against this, so I'll make it so.
How about the case of the constants: true, false vs. True, False? I like the latter, which match None. They would also stand out in the new docstrings mentioned by Fred.
Ditto.
How about operator.truth()? Has Tim changed his mind at all? Has Guido?
I don't know about Tim. I've made an executive decision that operator.truth() will return a bool (making it an alternate spelling for bool()).
If this PEP were implemented and "all built-in operations that conceptually return a Boolean" were updated, what verifiable examples of code breakage would we have?
I've changed the list of operations explicitly mentioned in the PEP (comparisons, some builtins, some string and file methods, a few other things). It breaks about 12 tests in the test suite. I haven't looked at all of these in detail, but I believe they are all shallow, of the following form: a test does "print x == y" and then the output is matched to "expected" output. The expected output strings must be changed from 0/1 to False/True.
The issue of dictionary keys has been raised, and is a bit perplexing, but not overly so, IMO. However, I'd like confirmation that this is indeed the intended behavior:
d = {} d[0] = 'First' d {0: 'First'} d[False] = 'Second' d {0: 'Second'} d[0.0] = 'Third' d {0: 'Third'} d[()] = 'Fourth' d {0: 'Third', (): 'Fourth'} d[None] = 'Fifth' d {0: 'Third', (): 'Fourth', None: 'Fifth'}
Yes.
Or are there any situations like this where you would want True and False to be a bit more like None and a bit less like ints? Meaning True and False could be dictionary keys separate from 0 and 1.
No. That would break backwards compatibility. False==0, and True==1; everything else follows from that. (But False is not 0, and True is not 1!)
Other than that, are there any concrete issues that have been raised that need to be addressed by the PEP? I understand the uneasy feeling felt by many as I felt it myself. However, I've read all the newsgroup postings and reread the PEP and I don't feel very queasy any more. I think this PEP is a step in the right direction. I would hope that clarifying some of the genuine issues might help to bring about consensus and reduce some of the rhetoric.
There's an educational issue that I'm just getting my head around. It appears that for various psychological and linguistic reasons, newbies naturally write if b == True: ... when they first encounter a bool variable and have to test whether it's true. I believe that this is most of Laura's issue. The only reasonable solution for this is that the Zen master slaps them over the head with a stick, and they learn to write if b: ... I'll update the PEP. --Guido van Rossum (home page: http://www.python.org/~guido/)
[Guido van Rossum]
My thoughts haven't changed at all. :-)
The first round of discussion (on python-dev) brought up some contentious issues where people disagreed strongly with particular details proposed in the PEP, and I put those contentious issues at the top of the review list. If you go to the latest version of the PEP on the web (http://www.python.org/peps/pep-0285.html) you'll see that I've added some other issues that were brought up in the more recent discussion.
I apologize for not having read the web version. I wasn't aware that it was being updated during the debate. I have a feeling I'm not alone in this mistake, so I'll take this opportunity to encourage others to read the web version also.
There's an educational issue that I'm just getting my head around. It appears that for various psychological and linguistic reasons, newbies naturally write
if b == True: ...
when they first encounter a bool variable and have to test whether it's true. I believe that this is most of Laura's issue. The only reasonable solution for this is that the Zen master slaps them over the head with a stick, and they learn to write
if b: ...
Yes. And I think describing Python's handling of truth values as "something vs. nothing" has real merit as a learning aid. It will certainly keep me from falling into the if b == True: trap. In summary, I agree with your clarifications and I'm +1 on the PEP. --- Patrick K. O'Brien Orbtech
[pobrien@orbtech.com]
How about operator.truth()? Has Tim changed his mind at all? Has Guido?
Guido change his mind? That's about as likely as Tim changing his <wink>. Guido Pronounced on this and that's good enough for me. I'd still prefer operator.truth() return 0/1, but this always was (as the PEP said from the start) a "minor additional issue". People can learn to use int(bool(x)) when they need 0/1, and operator.truth() becomes dead weight.
On Saturday, March 30, 2002, at 04:39 PM, Guido van Rossum wrote:
Abstract
This PEP proposes the introduction of a new built-in type, bool, with two constants, False and True. The bool type would be a straightforward subtype (in C) of the int type, and the values False and True would behave like 0 and 1 in most respects (for example, False==0 and True==1 would be true) except repr() and str(). All built-in operations that conceptually return a Boolean result will be changed to return False or True instead of 0 or 1; for example, comparisons, the "not" operator, and predicates like isinstance().
On a side note, writing 'i or False and True' performs much better than bool(i) in the test implementation, and flow control statements involving ints are perform much better than flow control using bool. Will there be a performance degredation in the final version, or can this be avoided?
1) Should this PEP be accepted at all.
-0 The only advantage I see is that there is a standard type for jython and other tools that need a boolean type can use. This is also a disadvantage, as all we really have is an object that may magically turn into an int if you are not careful. If you actually need a bool, you will always need to cast it 'just in case'. The only reason there is to modify existing code to return a bool rather than an int is so that repr(1 > 0) == 'True'. Consider: a = getflag() if a is None: print 'Unset' elif a is True: print 'True' elif a is False: print 'False' The above code reads well, but is incorrect unless we are sure what we are going to find in 'a'. The correct way to write the code is exactly like we do currently - adding a bool type adds nothing to Python code. I think of truth in Python is a state.
Most other details of the proposal are pretty much forced by the backwards compatibility requirement; e.g. True == 1 and True+1 == 2 must hold, else reams of existing code would break.
Minor additional issues:
4) Should we strive to eliminate non-Boolean operations on bools in the future, through suitable warnings, so that e.g. True+1 would eventually (e.g. in Python 3000 be illegal). Personally, I think we shouldn't; 28+isleap(y) seems totally reasonable to me.
non-Boolean operations should be eliminated eventually, so True + 1 raises a value error and True + True == True. 28+isleap(y) is an abuse of a side effect. If you want to add the return value to an integer, then you should return an integer and name your function correctly. 28+leapdays(y) is correct in my book, or 28+int(isleap(y)) if you don't live in Sweden.
Most languages eventually grow a Boolean type; even C99 (the new and improved C standard, not yet widely adopted) has one.
Many programmers apparently feel the need for a Boolean type; most Python documentation contains a bit of an apology for the absence of a Boolean type. I've seen lots of modules that defined constants "False=0" and "True=1" (or similar) at the top and used
Many programmers also feel the need for strong static typing in the language too - and I feel that this is intimately related to this PEP. The advantage of a bool type in a language like Java is that if I try to add an integer to a bool, I get a compile time error. Also note that, at least in C and Java, 'and' and 'or' return a boolean value - we can perform a series of boolean operations and know we will end up with 0 or 1 in C, and a boolean in Java. In Python we could end up with anything, and this behavior is relied on as one of the strengths of the language. There is current a concept of true and false as a state, not a value (or as another said, Something or Nothing).
It has been suggested that, in order to satisfy user expectations, for every x that is considered true in a Boolean context, the expression x == True should be true, and likewise if x is considered false, x == False should be true. This is of course impossible; it would mean that e.g. 6 == True and 7 == True, from which one could infer 6 == 7. Similarly, [] == False == None would be true, and one could infer [] == None, which is not the case. I'm not sure where this suggestion came from; it was made several times during the first review period. For truth testing of a value, one should use "if", e.g. "if x: print 'Yes'", not comparison to a truth value; "if x == True: print 'Yes'" is not only wrong, it is also strangely redundant.
This reads like an illustration of false logic. 'My dog is brown. My cat is brown. Therefore by dog is my cat'. -- Stuart Bishop <zen@shangri-la.dropbear.id.au> http://shangri-la.dropbear.id.au/
On Thursday, April 4, 2002, at 02:22 PM, Stuart Bishop wrote:
On a side note, writing 'i or False and True' performs much better than bool(i) in the test implementation, and flow control statements involving ints are perform much better than flow control using bool. Will there be a performance degredation in the final version, or can this be avoided?
I fixed my test to not do a global lookup on True and flow control is no longer slower. now-+1-if-anyone-cares-but-lets-see-what-another-100-posts-do-ly y'rs -- Stuart Bishop <zen@shangri-la.dropbear.id.au> http://shangri-la.dropbear.id.au/
On a side note, writing 'i or False and True' performs much better than bool(i) in the test implementation, and flow control statements involving ints are perform much better than flow control using bool. Will there be a performance degredation in the final version, or can this be avoided?
That surprises me. Can you show your benchmark? (I don't have the time to respond to the rest of your arguments, which have been brought up and responded to enough times in this discussion.) --Guido van Rossum (home page: http://www.python.org/~guido/)
On a side note, writing 'i or False and True' performs much better than bool(i) in the test implementation, and flow control statements involving ints are perform much better than flow control using bool. Will there be a performance degredation in the final version, or can this be avoided?
Guido> That surprises me. Can you show your benchmark? Doesn't 'i or False and True' always evaluate to 'i', rather than to 'bool(i)' because 'and' binds more tighly than 'or'? What's the corresponding performance measure for 'i and True or False'? -- Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark
participants (17)
-
Aahz
-
Andrew Koenig
-
barry@zope.com
-
David Abrahams
-
Fred L. Drake, Jr.
-
Fredrik Lundh
-
Guido van Rossum
-
Jeremy Hylton
-
Ka-Ping Yee
-
Kevin Jacobs
-
Mark McEahern
-
martin@v.loewis.de
-
Patrick K. O'Brien
-
Samuele Pedroni
-
Stuart Bishop
-
tanzer@swing.co.at
-
Tim Peters