Dear Guido: I would first like to thank you for giving us an opportunity to respond. I have spent most of the weekend thinking and writing a reply to this, and I think that this has made me a better teacher. For this I am grateful. I realise that this is rather long, but I have condensed it substantially from my efforts earlier this weekend. Thank you for taking the time to read this. Laura Creighton ----------------------------- I am opposed to the addition of the new proposed type on the grounds that it will make Python harder to teach both to people who have never programmed before and to people who _have_. If they have no preconceived idea of Booleans, then I do not propose to need to teach it to them in an introductory lesson. There is a time to learn symbolic logic but while trying to learn how to program for the first time is not it. If, on the other hand, they _do_ have some preconceived idea of Booleans then Python will not have what they want. They will want stricter booleans that 'behave properly'. Maybe some day we should give these people such a type. People who do symbolic logic and make push-down automatons all day long will love it. If we implement this, not out of objects, but out of bits, and have something really sparse in memory consumption, we will cause jubilation in the NumPy community as well. But I don't want to discuss this here, I only want to discuss the new type which is proposed in this PEP. And I believe that this half-way thing, int-in-hat, that is called bool but which does not implement what a mathematician would call truth values will make it much harder for me to teach the Python language and programming in general. Not only do I not need, but I actively do not want this change which is the integer 0 and the integer 1 that have some odd printing properties. 1) Should this PEP be accepted at all. No. 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".) Now we have a teaching problem. If str(True) returns anything but 'True' then I am going to have to explain this to newbies, really early on. I can't see myself claiming that 1 is the string representation of True. I can see myself explaining that int(True) is 1, or that bool(1) is True. If I say that the string representation of True is 1, then I must assert that True is just a fancier, prettier way of writing 1. But this breaks the more common practice where str is used for the prettier way of writing things and repr is used for the uglier one. And I guarantee my students will notice this, especially if they have heard me explain why their floating point numbers are not printed the way that they expect. I don't want to have to explain that eval(repr(object)) is supposed to generate the object whenever possible, for the last thing I want newbies to be thinking about is eval(). I guess I am going to have to say that it is a wart on the language, and that we have it this way so as not to break too much exisiting code. I think this wart is far uglier than the lack of a half-way-but-not- quite Boolean Truth value. But I am all for this wart rather than break the exisiting code. 3) Should the constants be called 'True' and 'False' (corresponding to None) or 'true' and 'false' (as in C++, Java and C99). I would rather the constants, if we have them, be called anything other than True or False. It is not the things that you don't know that hurt you the most in learning a language -- it is 'the things that you know that ain't so'. Learning C, from PL/1, my first problem was 'how do I write a procedure'? You see, I aleady _knew_ that _by definition_ functions returned values and procedures didn't, and so, since I didn't want to return a value, I didn't want to write a function. Learning that your basic defintions are wrong generally requires a poke from the outside. The same thing happened to me when learning Python. I knew, by definition, that attributes were not callable. Using getattr() to test whether a class had a method did not occur to me, despite pouring over python docs for 2 days looking for such a thing. Finally I went after it out of the __class__ -- and immediately posted something to python-list, saying 'This is ugly as sin. What do real people do?' I never got around to questioning whether 'attributes by definition, may not be a callable' in precisely the same way I never got around to questioning whether 'you could write a function that did not return a value'. The problem is that _everybody_ has some conceptual understanding of True and False. And whatever that understanding is, it is unlikely to be the one used by Python. Python does not distinguish between True and False -- Python makes the distinction between something and nothing. And I think that this is one of the reasons why well-written Python programs are so elegant. And this is what I am trying to teach people. So I out-and-out tell people this. {} is a dictionary-shaped nothing. [] is a list-shaped nothing. 0 is an integer-shaped nothing. 0.0 is a float shaped nothing. I want to save them from the error of writing if (bool(myDict) == True): and if they start out believing that python only distinguishes between Something and Nothing, they mostly are ok. And to rub this point in you can do this:
False = 1 True = 0 if False:
... print 'Surprise!' ... Surprise!
if True:
... print 'I am True'
... else:
... print 'Surprise Again!'
...
Surprise Again!
This is a very nice eye-opener. It is a true joy. Watch the minds go
*pop*! and the preconceived notions disappear. (You then let them know
exactly what you will think of them if they ever do this 'for real', of
course.)
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.
This is not an argument for allowing non-Boolean operations on bools();
this is an argument for not writing functions that return Booleans. Make
them return numbers instead, so that you can use them as you did. Last
month we discussed why in 1712 February had 30 days in Sweden. (See:
http://groups.google.com/groups?q=leap+year+Sweden+group:
comp.lang.python.*&hl=en&selm=Xns91D08815184B2svenaxelssonbokochwe%
40212.37.1.234&rnum=1
if you care, and you missed it.)
I live in Sweden. Assigning students the problem of calculating whether
or not a given year is a leap year in Sweden appeals to me.
But I know students. I guarantee that I will get an isleap that returns
a True or a False under the proposed new regime. And this is precisely
what I do not want, which I will try to teach by assigning, next week,
a program that calculates how many days are in a given year. I predict
that I will get a lot of answers like this:
if year == 1712:
days = 367
elif isSwedishLeap(year):
days = 366
else:
days = 365
There are many things wrong with this code. The bizzare special case of
1712 belongs in the SwedishLeap function, with the rest of the
weirdnesses. Thus I will have to convince my students that it is better
to write a function that does not return True or False. And this is
despite the fact that I originally asked for 'is Y a leap year or not'.
This will be a necessary exercise. If True and False are in the
language, I am going to have to work especially hard to teach my
students that you (mostly) shouldn't use the fool things. There is
almost always some value that you would like to return instead.
Having renamed SwedishLeap and fixed it to return how many leap days,
instead of a bool, I have now made a different problem for myself and
my students.
The new improved solution for 'is this year a leap year' will be:
if bool(SwedishLeap(year)) == True:
# the better students will say 'is' instead of '=='
print 'yes.'
else:
print 'no.'
Aaargh!
I already see too much code like this. It's mostly written by people
who come from other languages. They define their own True and False so
they can do this. (And they mostly have an extra set of () as well).
Right now I have the perfect fix for this. I just say 'Python does not
care about True and False, only nothing or something'. You have just
stolen my great weapon.
What am I going to say?
attempt 1.
Python pretends to have bools, but they are just ints in fancy hats. So
you are making more comparisons than are necessary.
smart student:
But you said it is better to be explicit than implicit! And here I am
explicitly performing the type coercion rather than let it happen
implicitly! (or PyChecker _warned_ me that I was making an implicit
conversion!) I put in the cast so that people will know exactly what
is happening!
attempt 2.
But they are _really_ ints 'under the hood'. I was not kidding about the
fancy hats! if SwedishLeap(year): is precisely what you want. You
don't want to test against True at all!
smart student:
Comparisons yield boolean values. Therefore they _want_ a Boolean
value. You are just being lazy because it saves typing. In the bad old
days before we had Booleans this was ok, but now that we have them we
should use them! Otherwise what good are they? What should I be using
them for if not for this?
attempt 3.
Got an hour? I'd like to explain signature-based polymorphism to you ...
smart student:
ha! ha! ha!
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 think that operator.truth(x) should return an int because about the
only thing I use it for is operator.truth(myDict). I really want the
integer value. How do you propose I get it if you change things? Why
make me go through the gyrations of int(bool(myDict))?
By the way, look at the list of those things that would be changed
to return a bool. Most of them are python implementations of ANCIENT
C functions. They date from the time before we invented exceptions!
The primitive old days when we had to test for every possible time
you wouldn't want to run your code before you actually got to run it.
I don't want to go back to the days of
if aflag or (bflag and (cflag or dflag)):
either.
And all Truth testing (unless you are doing symbolic logic) reeks
this way to me, of the old style I am trying to stamp out. Truth
testing is just another form of type testing, and just as ugly.
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.
So fix the docs, don't change the code! <wink>. I think the fact that
in python control flow structures distinguish between Something and
Nothing is one of the beauties and glories of the language, and you
should delete any documentation that says otherwise.
Under the proposed new scheme you will have to trade apologies for the
lack of bools, for apologies for not producing real bools, only this
int-in-a-new-hat hack that pretends to be a bool. This is hardly
progress.
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.
So would adding True and False to the __builtins__, and probably
operator.truth as well, and then modifying PEP 8 saying to use the
things if you actually have a need for True and False. Then you could
also get a much needed word in edgewise discouraging
if bool(x) == True:
or actually using True and False much, because there is usually a better
more pythonic way to do what people used to other languages are accustomed
to doing with booleans. This is precisely what some people have said
here: 'When I started using Python, I made True and False, but once I
stopped trying to program in some other language using python, I
stopped needing these things'. (see the recent post by Don Garrett