On Tue, Aug 24, 2021 at 3:27 PM Christopher Barker
On Mon, Aug 23, 2021 at 6:54 AM Thomas Grainger
wrote: here's another fun one "A False midnight": https://lwn.net/Articles/590299/ https://bugs.python.org/issue13936#msg212771
This is a great example of the problem of the assumption of zero as representing false.
More a problem with the assumption that midnight is zero. There's nothing at all wrong with zero being false - if, for instance, you're looking at a timedelta, a width of zero seconds is an empty time interval, and that can indeed be false. (Consider: What is the overlap between segment X and segment Y? Find the intersection between them; if the intersection has zero width, there is no overlap and the two time periods do not collide.)
I’ve always been ambivalent about Python’s concept of Truthiness (“something or nothing”). If I were to write my own language, I would probably require a actual Boolean for, eg, an if statement.
That becomes very frustrating. Before you design your own language, I strongly recommend trying out REXX, C, Python, LPC or Pike, JavaScript, and SourcePawn, just because they have such distinctly different type systems. (Obviously you'll have tried some of those already, but try to complete the set.) And by "try out", I mean spend a good amount of time coding in them. Get to know what's frustrating about them. For instance, what does "if 0.0" mean? (Python: False. C: False. JavaScript: False. Pike: True. REXX: Error. SourcePawn: False, but can become True if the tag changes. SourcePawn doesn't have types, it has tags.) Similarly, what is the truth value of an empty array (or equivalent in each language? (Python: False. C: True. JavaScript: True. REXX: Concept does not exist. SourcePawn: Error. Pike: True.) Not one of these languages is fundamentally *wrong*, but they disagree on what logical choices to make. (Yes, I'm being fairly generous towards SourcePawn here. Truth be told, it sucks.) If you're going to make changes from the way Python does things, be sure to have tried a language that already works that way, and see what the consequences are.
The fact is that what defines falsiness is use case dependent. Numbers are the best example, zero. A often be a perfectly meaningful number.
Of course it's a perfectly meaningful number, but you won't be saying "if x:" to figure out whether x is a meaningful number. The question is, what *else* does it mean? For instance, if you're asking "where in this string does the letter w first occur?", then 0 is a perfectly meaningful result, indicating that it's found at the very start of the string. But that's not about whether it's a number; it's whether it's a string index.
Which id why Brandon suggested that testing the length of a sequence was a good way to be explicit about what you mean by false in a particular context.
When people say "explicit is better than implicit", they usually mean "code I like is better than code I don't like". And then they get (somewhat rightly) lampooned by people like Steven who interpret "explicit" to mean "using more words to mean nothing", which clearly violates the Zen. What ARE you being explicit about? Are you stating the concept "check if there are any users", or are you stating the concept "take the list of users, count how many people there are in it, and check if that number is greater than zero"? The first one is written "if users:" and the second "if len(users) > 0:". They are BOTH explicit, but they are stating different things. The point of a high level programming language is that we can express abstract concepts. We don't have to hold the interpreter's hand and say "to figure out how many people are in the list, take the past-end-of-list pointer, subtract the list base pointer, and divide by the size of a list element". We say "give me the length of the list". And one huge advantage is that the interpreter is free to give you that length in any way it likes (directly storing the length, using pointer arithmetic, or even iterating over a sparse list and counting the slots that are occupied). Only be "more explicit" (in the sense of using lower-level constructs) if you need to be.
So explicitly specifying that you are looking for len(container) == 0 is more clear than isempty(container) would be, even if it did exist.
I'm not sure that that's any better. One is asking "how much stuff is in the container? Zero items?" and the other is asking "is this container empty?". They're different concepts. They will (usually) give the same result, but conceptually they're different, and it's not a scale of "more explicit" to "less explicit".
With duck typing, you may well not know what type you are dealing with, but you absolutely need to know how you expect that object to behave in the context of your code. So if having a zero length is meaningful in your code — then only objects with a length will work, which is just fine.
Yes. And it should make perfect sense to write something like this: if stuff: print("My stuff:") for thing in stuff: print(thing) where you omit the header if there are no elements to iterate over. What is the type of "stuff"? Do you care? Well, it can't be a generator, since those will always be true; but it can be any sort of collection. Be explicit about what you care about. Don't shackle the code with unnecessary constraints, and don't hand-hold the interpreter unnecessarily. ChrisA