18 ms·
Please reconsider the Boolean evaluation of midnight
- unoti 13y agoI just got bit by this a few days ago. I was creating an event scheduling system that uses either repeating entries with a datetime.time, or one time entries with a datetime.datetime. I had code that said "if start_time" to see which it was, and discovered later that midnight evaluates to false. It's not the best idea.
- einhverfr 13y agoWhat's actually interesting if you dig into the discussion is that the actual behavior depends on your timezone. Basically from UTC east to the IDL, it behaves as you describe it. Anything west, it evaluates to true as long as it is set and there is a timezone offset. This is because once you are at a negative offset, you subtract and get a higher number so it will never evaluate to false with a negative offset. Basically this current behavior is useless for anything I can imagine. You can't use it to check for midnight UTC. You can't use it to check for whether it is set. You can probably use to check for midnight with no timezone but add a timezone and now your handling is messy. This is why contracts need to be sanely written from a code contract point ahead of time. This was apparently a developer describing how his code worked and later calling it a contract, which is a very bad thing :-D
- deleted 13y ago[deleted]
- dec0dedab0de 13y agoOff the top of my head I can't think of a reason to check if a date exists, but I would certainly expect midnight to be truthy if I found a reason.
- ojii 13y agoIn Django you might have a nullable TimeField and check if it's set via "if obj.time_field".
- calpaterson 13y agoI always write "if x is None", trying to be explicit.
- danielsamuels 13y agoWhat about optional date fields in a Django application?
- isaacremuant 13y agoyou check if it's None or for the type.
- nas 13y agoWhile I agree this is surprising behavior and I wouldn't design an API this way, it is documented behavior. From the docs: "in Boolean contexts, a time object is considered to be true if and only if, after converting it to minutes and subtracting utcoffset() (or 0 if that’s None), the result is non-zero" Changing at this point would possibly break code that relied on documented library behavior. That's not a responsible thing to do.
- RyanMcGreal 13y agoExplicit is better than implicit. Simple is better than complex.
- nas 13y agoI'm not sure what that is supposed to mean in this context. Testing for "is not None" is more explicit and avoids the trap being discussed. I don't know how the second sentence applies, maybe "if timeval:" is simpler? BTW, do you know the original author of the Zen of Python has posted to this very dicussion (while also being the orginal author of said module): https://mail.python.org/pipermail/python-ideas/2014-March/026573.html https://mail.python.org/pipermail/python-ideas/2014-March/02... https://mail.python.org/pipermail/python-ideas/2014-March/026577.html https://mail.python.org/pipermail/python-ideas/2014-March/02...
- Pacabel 13y ago"Simplicity" is not measured in terms of key presses or characters. The "if timeval:" case may contain fewer characters, but it's less explicit. Being less explicit opens it up to greater ambiguity. Ambiguity is a form of complexity. Complexity is the opposite of simplicity. The explicit "is not None" check may require more typing, but it's far more explicit and exact. That means it's much less ambiguous, and thus less complex, and thus exhibits greater simplicity.
- freyrs3 13y agoThe word "simple" means many things to many people, it's probably the most ambiguous line in the Zen of Python.
- deleted 13y ago[deleted]
- wzdd 13y agoLots of Python objects are falsey: empty lists, empty strings, etc. So it's never a good idea to write "if <thing>" when you mean "if <thing> is not None". This is pretty well-known, I thought.
- randlet 13y agoA big part of it is about user expectations. I would be shocked if any experienced Python programmer who wasn't familiar with this exact implementation detail expected the following: if datetime.time(0, 0, 0): print "foo" else: print "bar" to print "bar"
- mzs 13y agoOh man people that have used python for a while have come to expect datetime to just be strange in general. Essentially the default is to be in your TZ from midnight, you need to convert and there are different classes for epoch, and some of the members seemingly willy-nilly are not there or named differently then! And that's just the start, that's why things like arrow exist: http://crsmithdev.com/arrow/ http://crsmithdev.com/arrow/ time and datetime in python show their age.
- ameoba 13y agoFalsey objects mean "there's no data here" which is why they're falsey. Having an empty list of arguments to a command is the same as having no argument list. There's nothing about midnight that makes sense for it to behave as Falase.
- Nanzikambe 13y agoWhilst reading that thread, I stumbled accross: "goto fail" is a well-known error handling mechanism in open source software, widely reputed for its robusteness: http://opensource.apple.com/source/Security/Security-55471/libsecurity_ssl/lib/sslKeyExchange.c https://www.gitorious.org/gnutls/gnutls/source/6aa26f78150ccbdf0aec1878a41c17c41d358a3b:lib/x509/verify.c I believe Python needs to add support for this superior paradigm. It would involve a new keyword "fail" and some means of goto'ing to it. I suggest "raise to fail": if (some_error): raise to fail fail: <error handling code> Unless there are many objections, this fantastic idea might be submitted in a (short) PEP somewhere around the beginning of next month. There is some obvious overlap with the rejected "goto PEP" (PEP 3163) and the Python 2.3 goto module. However, the superiority of goto fail as error generation and error handling paradigm has since then been thoroughly proven. https://mail.python.org/pipermail/python-ideas/2014-March/026581.html https://mail.python.org/pipermail/python-ideas/2014-March/02...
- mercurial 13y agoPicking on C's error handling mechanism is like picking on its non-existent standard library: it's been done to death.
- kingkilr 13y agoPython already comes with the logical conclusion of goto-fail out of the box. There's no need to add a new special feature for it. (There is absolutely no certificate store checking for certs by default, nor is there any hostname checking, or any of the myriad of other checks one might expect a reasonable TLS implementation to perform. Use the requests module.)
- nbouscal 13y agoIf I understand the argument there correctly, the responder is saying: Nobody should ever use this functionality, instead they should always check that the date is not None. So, we should leave this broken, because we don't want to break backwards-compatibility with that class of applications that nobody should ever write. That philosophy, taken to its logical conclusion, results in everything being broken forever.
- tomp 13y agoEquivalently, taken to its logical conclusion, it results in more people writing better code.
- tehwalrus 13y agoif var: ... is better code (for my value of better, which is subjective) than: if var is not None: ...
- batmansbelt 13y agoBy what criteria have you decided that?
- tehwalrus 13y agoquicker to scan-read ("code will be read many more times than it is written or modified", being the relevant axiom)
- Iftheshoefits 13y agoReading code is a purposeful activity, though; namely, to understand what the code does. The speed with which that can be done is relevant in the margins from a productivity standpoint, but to build an argument for code structure around it strikes me as missing the point of the "axiom" you quote.
- mildtrepidation 13y ago
- colanderman 13y agoI've never seen a good argument for anything beside "false" to be considered false. Likewise for "true". Keystrokes are not a commodity for most coders, and compilers are not dumb; just be explicit and write "!= 0" or whatever. (And 0 == False, "" != False, but both 0 and "" are considered false? C'mon Python, that's borderline JavaScript territory.)
- nbouscal 13y agoI really don't think there is a good argument. Any time saved in writing the code is inevitably repaid in debugging it.
- maxerickson 13y agoWhy is it unreasonable for comparisons to behave differently than Boolean checks?
- rdtsc 13y agoPython has truthiness and falsiness as well. That shortens code like: if not x: do_something() x could be 0, None, False, empty set set(), {}, [], (), '',"" or an I believe any class or object that appropriately overrides __nonzero__() or __bool__() methods. It is a matter of taste, so I rather like it.
- nbouscal 13y agoIt's not a matter of taste when it breaks functionality.
- rdtsc 13y agoIt depends on what x is and what do_something() does. Most often than not in my code that wouldn't break functionality. In the case of dates it is broken, so same code pattern there would be broken. At this point a lot more functionality would break if 0 become non-False or threw a TypeError exception.
- famousactress 13y ago
- deleted 13y ago[deleted]
- jfb 13y agoCreeping semi-booleans make me very uncomfortable. But what's the alternative? A-values and I-values? A "μ" for questions unanswerable in the type system? Just punt and let Javascriptisms take over the world?
- delinka 13y agoNot being a Pythonista, I have the following questions: 1) Is there a (native or custom) date type in Python? Is it an object? 2) Midnight when? Today? This date last year? Sure there's a "zero value" for dates - it's the epoch for whichever platform or library you're using. 3) Why in would anyone call it a "date" if it's really a time? Maybe I'm getting off into the philosophical decisions of the reptile wranglers, but this particular debate sounds a lot like someone made a decision long ago that had ramifications further than expected and now the justification is engrained, things are built on it, and no one's willing to make the 'correction.'
- mildtrepidation 13y agoPython has dates, times, and datetimes[0]. They're all objects. So yes, you could get around this by always using a datetime. However, sometimes what you need is just a time. [0] http://docs.python.org/2/library/datetime.html http://docs.python.org/2/library/datetime.html
- donaldstufft 13y agoIt's midnight without a date, however if there is a timezone attached to the time then it's midnight utc unless the utc offset of the time is a negative value, then it's never.
- maxerickson 13y agoThe second paragraph of the link contains "datetime.time(0,0,0)". I can understand not wanting to make many inferences, but that sort of answers your questions.
- njharman 13y agoWhy would anyone evaluate dates in a boolean context? They are (should be) always True.
- csense 13y ago> They are (should be) always True. This is the obvious intuition, which doesn't match the current implementation. > Why would anyone evaluate dates in a boolean context? I'd guess, usually to tell whether a variable has a date or None. For example, maybe you have some kind of GUI with a form where the user is supposed to fill in a time, and some variable somewhere in the code is set to None [1] when the time field is blank [2], and a time object representing the time entered by the user otherwise. Maybe you want to require the user to fill in all fields in the form before advancing to the next step in your program's workflow. If you like falsiness, you might write: if not form.time_field: show_warning("You must enter a time to proceed") continue_current_phase() else: proceed_to_next_phase() You actually intended the first line to be: if form.time_field is not None: But the only way you'd find this bug is when a user on the East Coast complains that the form won't accept 7:00 (UTC midnight). It will accept 6:59 or 7:01, but not 7:00. Of course you're on the West Coast, so you close their ticket as "can't reproduce" since 7:00 works just fine for you... [1] None is the Python equivalent of other programming languages' null (or NULL). [2] Or unparseable
- hyperpape 13y agoJames Coglan recently pointed out that all of Python's falsy values are the additive identity of some type. Midnight fits the mold. This results in some weird results from an intuitive perspective, but is very principled and elegant in other ways. My one objection was that I don't know how None fits in.
- colanderman 13y agoHow is "midnight" an additive identity? It's not a time delta. "+0 hours" is an additive identity. (And if Python's time library doesn't distinguish between absolute and relative times, that's a design flaw.) That's like saying "north" is an additive identity on a compass. It's not; "+0° clockwise" is an additive identity.
- kingkilr 13y agoMidnight is not the addittive identity of points in time. There is no additive identity because there is no addition operation. You're confusing the ``time`` type, with the ``timedelta`` type, which represents a duration, and does have 0-minutes as an additive identity. midnight is not, nor has it ever been, the "zero-time", it's simply a point whose typical representation contains some 0s. It is no more Falsey than the origin (0, 0) in the cartesian coordinate system is Falsey.
- hyperpape 13y agoYou win: I was confusing those things! I was thinking times were treated as a timedelta ranging from 0 to 23 hours 59...
- donaldstufft 13y agoTo take it from the same James Coglan -> "You can't add midnight to 3-o'clock in the same way you can't add London to Chicago."
- obsurveyor 13y agoAs far as I understand from reading that whole thread, this is, ironically, precisely why local midnight evaluates to False. They left in an escape hatch to support the illogical idea of addition.
- abvdasker 13y agoOn the plus side, Boolean Value: Midnight would make a great CS-themed action movie title.
- mark-r 13y agoIn every other language I've used, a time value of 0 is used when a datetime only contains a date and doesn't have a specific time. The existing behavior would make sense in that context. I know Python also has a separate date object, are the two interchangeable enough that you could mix and match without problems?
- josephlord 13y agoI think the interesting part is what is revealed about Python and the difference with something like Ruby. Python is stable[0] and places a high degree of importance on backwards compatibility. This behaviour is well documented (and called out for particular note). This reinforces that it is (a) official and (b) not a bug because it is the documented behaviour. On the other hand Ruby (and most Ruby libraries) seem both less concerned with backwards compatibility, have less thorough documentation[1] but are more willing to change and improve. There isn't a right and a wrong between these approaches although for most things I think I would prefer something between the two. I think I generally prefer Python in terms of syntax (Ruby is a bit too flexible with too many ways to do things for my taste) but I do wonder if Python will be left a little behind. [0] Python 2/3 transition is a single big deliberate change. [1] I have an open Rails issue that I don't know if is a bug or not because there isn't documentation that is sufficient to compare the behaviour with so it is a case of what feels right/wrong: https://github.com/rails/rails/issues/6659 https://github.com/rails/rails/issues/6659
- sanderjd 13y agoI'm gonna disagree with you! Well, I actually agree about ruby libraries, but the ruby standard library is the proper comparison here, which in my experience manages backwards compatibility and documentation of edge cases rather nicely. The (many) changes to the language itself since 1.8 have nearly all happened in backwards-compatible ways. So maybe the ruby community has a more flexible attitude (I've honestly never used enough external python libraries to have a good comparison point), but I don't think it's fair to say that the language itself is.
- josephlord 13y agoI was deliberately discussing the whole library ecosystem you could well be right about the stability of the core of Ruby.
- tomchristie 13y agoSo ignoring the hype, here's the outcome-to-date... The ticket was reconsidered, reopened and classified as a bug. http://bugs.python.org/msg212771 http://bugs.python.org/msg212771 Nick Coghlan's dissection of the issue here: https://mail.python.org/pipermail/python-ideas/2014-March/026647.html https://mail.python.org/pipermail/python-ideas/2014-March/02... is pretty much perfect - wonderful piece of technical writing! Donald Stufft has expressed an interest in making the patch for this happen, and assuming all goes as planned this usage will raise a deprecation warning in 3.5 and be fully fixed in 3.6. News in brief: User raises issue. Issue gets resolved.
- sanderjd 13y agoNeat! This stuff actually works sometimes!
- Grue3 13y agoWill it be resolved in 2.x though? That's what most people care about.
- dtech 13y ago2.x is EOL and only remains for legacy programs. A backwards-incompatible change won't even be considered for this branch.
- username223 13y agoMy guess is that eventually someone with enough money on the line will be sufficiently harmed by the Python 3 nonsense to maintain 2.x, at which point obvious bugfixes like this will be "ported" (it's probably a 2-line change).
- Perseids 13y ago> That's what most people care about. I'll make an equally aggressive counter point: Dammit guys and gals, stop using 2.x. Python 3 came out more than 6 years ago. You are holding back and splitting the community. And if you tell me 3.x has no perceived value then this is the value that is slowly stacking up.
- eq- 13y agoThe only reason for midnight being a falsy value that I can think of is that someone thought that all objects should provide some functionality for __nonzero__/__bool__. It was a bad idea.
- platz 13y agoboolean blindness http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/ http://existentialtype.wordpress.com/2011/03/15/boolean-blin...
- spacemanmatt 13y agoI think he understates the most powerful part of his argument. Midnight is a value, not a special value. There is no reason why it or any other valid time should be falsey on a daily cycle.
- nostromo 13y agoCouldn't the same argument be made about zero?
- lutusp 13y agoIt seems there are two choices: 1. Before applying a numerical value to a Boolean test, ask whether it can ever be zero when that's not the intent of the test. 2. Create a new rule that forbids testing numerical values as though they're Booleans, and break nearly every program in existence. Hmm ... wait ... I'm thinking it over.
- bouk 13y agoPython has weird ideas about comparisons, I'm pretty sure it's the only language where this is possible: https://eval.in/113749 https://eval.in/113749
- clarkevans 13y agoINADA Naoki's argument [1] is succinct and insightful. I feel zero value of non abelian group should not mean False in bool context. () + () == () "" + "" == "" 0 + 0 == 0 timedelta() + timedelta() == timedelta() time() + time() => TypeError [1] https://mail.python.org/pipermail/python-ideas/2014-March/026674.html https://mail.python.org/pipermail/python-ideas/2014-March/02...
- barakm 13y agoCame here to say this. It's the most useful statement in the entire thread of "you're wrong!" "no, you're wrong!" and nobody noticed.
- deleted 13y ago[deleted]
- dalke 13y agoPerhaps people ignored it because it isn't true? {}+{} fails, because addition of dictionaries is non-abelian and Python refuses to choose a preferred result. But bool({}) is perfectly reasonable.
- maxiepoo 13y agoAbelian group is a sort of stringent requirement, they probably just mean a plain monoid? After all it works for python lists, which form a non-commutative monoid: [] + [] == [] [1,2] + [3] != [3] + [1,2]
- jessaustin 13y agoThe additional property which makes a monoid a group is the existence of inverse elements. So list is a good example for your point, because there are no "inverse lists".
- raverbashing 13y agoAnd this is why programmers who know math are better programmers.
- mannykannot 13y agoThis offers a counterexample to the simplistic notion that 'duck typing' results in programs that automagically do the right thing. The reality is that duck typing does not relieve you of the responsibility of understanding the semantics of the elements you use to construct a program from.
- Ideka 13y agoThis kind of crap is exactly the reason why I don't like doing just "if var:" unless var is guaranteed to be a boolean.
- murbard2 13y agoDeprecate datetime and introduce datetime2 with better behavior for midnight. Problem solved.
- joelthelion 13y agoThis is how languages die. I wasn't aware that Python had become such a bureaucracy. The current behavior is insane - just fix it! No need for days of discussion on the mailing list or three-point non regression plans.
- ggchappell 13y agoIgnoring Python for a bit and thinking as a designer of some hypothetical future language: there is a nice rule given here for evaluation in a Boolean context. I wonder whether it should be taken as a general guideline for future languages. The rule, in its entirety, is this: - Booleans are falsy when false. - Numbers are falsy when zero. - Containers are falsy when empty. - None is always falsy. - No other type of value is ever falsy. I can think of two ways we might possibly want to alter the rule. The first is to expand the idea of number to include arbitrary groups (or monoids?), with the identity element being falsy. So, for example, a matrix with all entries zero might be falsy. Or a 3-D transformation might be falsy if it does not move anything. The second is one I have encountered in C++. There, an I/O stream is falsy if it is in an error state. This makes error checking easy; there is one less member-function name to remember. We might expand this idea to include things like Python's urllib, or any object that wraps a connection or stream of some kind. EDIT: OTOH, there is the Haskell philosophy, where the only thing that can be evaluated in a Boolean context is a Bool, so the only falsy thing is False. EDIT 2: The comment by clarkevans (quoting a message from INADA Naoki) already partially addressed the above group idea: "I feel zero value of non abelian group should not mean False in bool context."
- StefanKarpinski 13y agoMaking anything besides booleans "truthy" strikes me as asking for a lot of trouble for very little gain. For each of these cases, how hard is this to write: x == 0 isempty(x) x == nothing How much clearer is the intent of that code than a truthy boolean test would be? This clarity is all the more important in dynamically typed languages without type annotations since the code itself doesn't give any hint what the type of `x` might be, so the programmer doesn't know what it is the test is really checking for. Oh the other hand, the cost of truthiness is pretty significant. Each person reading or writing code in a language with truthiness must remember all the arbitrary truthiness rules that particular language uses – and they're quite different for each of Python, Perl, Ruby, C, JavaScript, etc. As this issue demonstrates, whenever you open the door a little, even in a generally sane language like Python, at some point some weird decisions get made and you find yourself with some strange corner cases like midnight being falsey. If you wanted to know if a date was midnight, wouldn't it be easy enough to just explicitly test that?
- pistle 13y agoMidnight UTC is zero's all the way down. Seems false to me, but I'm from the land of C. This seems to be in line with some low level hardware or common assembly practice across many languages. Everyone is talking higher echelons of consideration, but what effect is there on generated byte code or in fitting within the virtual machine's tight pants?
- dools 13y agoThis is freakishly similar to the discussion on a PHP bug I submitted in 2006: https://bugs.php.net/bug.php?id=39579 https://bugs.php.net/bug.php?id=39579
- einhverfr 13y agoThis sub discussion seemed interesting to me: https://mail.python.org/pipermail/python-ideas/2014-March/026656.html https://mail.python.org/pipermail/python-ideas/2014-March/02... My first thought was this: http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements005.htm http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_el...
- Robadob 13y agoI came across a similar issue when using rails the other day, where I gave my model a boolean field that had presence validation. The presence validation of the boolean field fails if the bool is set to false, had me confused for a while, but It wasn't a big enough issue for me to research/report.