PEP 285: Adding a bool type

Guido van Rossum guido@python.org
Sat, 30 Mar 2002 00:39:10 -0500


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: