7 ms·
Epicycles. Experience leaves me inclined to believe the code precedes the definition. def all(iterable): for element in iterable: if not element:
by syphilis2 7y ago
Epicycles.
Experience leaves me inclined to believe the code precedes the definition.
def all(iterable):
for element in iterable:
if not element:
return False
return True
https://github.com/python/cpython/blob/master/Python/bltinmodule.c https://github.com/python/cpython/blob/master/Python/bltinmo...
- eru 7y agoBut that doesn't tell you _why_ that choice is a good idea! My favourite explanation comes from property based testing. In general, you have the following property: For any lists xs and ys, all(xs) and all(ys) == all(xs + ys) If you want to keep that property as simple as possible, you have to define all([]) == True If you want to go with the opposite, your property becomes more complicated. Mathematical definitions are somewhat arbitrary. It's up to us to choose definitions that make sense in our contexts. A general desire to make the resulting systems simple and uniform worked out well for many, many context. Slight detour: that's also why defining 2 - 5 to have an answer is a good idea! Negative numbers were seen as a bit suspect, but they behave perfectly sensible. Same for complex numbers. But: still we generally leave 0 / 0 undefined, because no answer would lead to a satisfying system.
- gridlockd 7y ago> But that doesn't tell you _why_ that choice is a good idea! That wasn't necessarily a choice, it may have just been an oversight not to deal with the "empty list" case. The fact that this blog post exists is a testament to this behavior being surprising. I don't think any of the arguments - philosophical or otherwise - are very good from a practical standpoint, that is: is this behavior is not more likely to cause harm? My gut feeling says that this behavior is somewhat unsafe, but then again Python is probably not the language to use when safety is a big concern. A hypothetical safer language might be better off to define "all/any" only for non-empty list.
- eru 7y agoHaskell, the prototypical careful language, defines their equivalents for 'any' and 'all' exactly like Python does it. And that's not an accident. > That wasn't necessarily a choice, it may have just been an oversight not to deal with the "empty list" case. If your general code gives you a answer for a corner case, it's a good hint that this might be a reasonable answer. Not a guarantee, though.
- gridlockd 7y agoFunctional languages tend to elevate some mathematical ideals before concerns of practical safety, performance or just common sense.
- eru 7y agoOn my cynical days, I'd be inclined to grant the latter two. But do you have an example of the first?
- gridlockd 7y agoIt boils down to the position that side-effects are actually totally fine when they allow you to write a simpler, more straightforward program, where state is easy to observe and debug. Such code is less likely to contain obfuscated logical errors arising from an attempt to not break "functional purity". It has a chance to be practically more safe. Another aspect is the way most functional languages treat memory. If you need a garbage collector, you effectively have to throw out hard realtime constraints. Latency and memory usage can be safety concerns. Most functional languages do not let you reason about that.
- lmm 7y ago> Latency and memory usage can be safety concerns. Most functional languages do not let you reason about that. Most languages of any kind do not let you reason about that. You're not wrong, but neither is this some specific issue with functional languages. Indeed I'd say functional languages are at the forefront of trying to bring in ways to reason about those, with things like Linear Haskell or https://www.microsoft.com/en-us/research/publication/coq-worlds-best-macro-assembler/ https://www.microsoft.com/en-us/research/publication/coq-wor...
- majewsky 7y agoall(xs) and all(ys) == all(xs + ys) If I understand you correctly, the "and" should be an "implies" instead: all(xs) => (all(ys) == all(xs + ys)) Otherwise this law cannot be true since there exist xs for which !all(xs).
- quietbritishjim 7y agoI think they just got their operator precedences mixed up. They meant: (all(xs) and all(ys)) == all(xs + ys) This is why you should always use parentheses unless it's absolutely obvious! I certainly had to check the docs to find out which way round it goes. I guess normally you're not dealing with Boolean variables so having == bind tighter makes more sense: y is not None and x == 7
- eru 7y agoYes, indeed! Sorry, I was deviating from the Python order of precedence.
- deleted 7y ago[deleted]
- deleted 7y ago[deleted]
- mehrdadn 7y agoI love this. But I feel like there are people (like you and me) who find an explanation like this awesome and compelling, and then there are others who have a hard time getting on the same wavelength, let alone to try to agree. Like for example, a similar sort of reasoning leads to min(x, y) breaking ties in favor of x, but max(x, y) breaking ties in favor of y, e.g. because [min(x, y), max(x, y)] should stably sort the objects. (There were discussions of how C++'s definition is broken a while back, if you Google hard enough.) But whenever I've tried to explain things like this to people, I've found so many just don't seem to see the issue, or to remotely care if they do. Their intuitive responses tend to be: - It's an obscure issue, it's never gonna come up. - Oh, well I mean it's obviously your fault for calling it that way with that implicit assumption. Why would you think that? - Well it's the caller's responsibility to read the documentation/look at the source/test the code before they use this function, not my problem to try to fit it into every mathematically possible use case. I feel like the notion of putting some more thought into what seems like an elementary API so that these issues don't come up and trip up users in the first place seems like a completely unjustifiable academic effort to them, if not an outright foreign one. Have you found any effective way to try to communicate stuff like this and hopefully actually convince people?
- deleted 7y ago[deleted]
- lmm 7y agoThe very concept of "breaking ties" with min/max suggests that something woolly and ill-defined is going on; surely x and y are either equal or they are not?
- mehrdadn 7y agoThat's everyone's instinctive reaction, but it's completely wrong, and everyone will know this too, if you just give the right examples. Like say you compare people by their age. That's a perfectly fine operation sufficient for sorting, min, max, etc. Now you find two people have the same age. Are they the same person? Or let's say you're sorting strings case-insensitively. "a" might be neither less than nor greater than "A", meaning they'd be unordered with respect to each other. But that doesn't mean you suddenly wouldn't care if one got duplicated and the other got deleted. Fundamentally "unordered" just means "neither comes before nor comes after". It doesn't imply "equal to". You can wrap this in a lot of fancy math terminology about partial and total orders, but it's not that complicated conceptually; the idea is just that the lack of an ordering doesn't imply equality, just like how you and your twin aren't the same person. This is one aspect of why C++ is introducing more sophisticated comparisons in C++20 (I would suggest looking it up).
- JadeNB 7y ago0/0 isn't the best example, because every answer leads to a satisfying system—i.e., it'd be perfectly fine to have a multiply valued quotient here; we'd just have to let it infect all the rest of our arithmetic operations. It's 1/0 and other such fractions that have no sensible answer.
- eru 7y agoI'm a bit confused. You can make 1/0 return infinity, and preserve some properties of fields. Though that works best, if your zeroes are also signed. But for 0/0, I don't see nearly as many properties you can rescue.
- JadeNB 7y agoWhatever value x you assign to 1/0, it had better be true that multiplying it by 0 gives 1; and the properties of a field give that 0x = (0 + 0)x = 0x + 0x, so that 0x = 0. You thus would have to give up either distributivity or additive inverses, which are pretty dear to me! If, on the other hand, you regard y = 0/0 as a "multi-valued variable" standing for any element of the field, then it behaves perfectly fine, since 0y = 0. The only problem is that it's infectious, so that just about any arithmetic computation involving it, like y + 1 or 2y, also suddenly has to stand for every element of your field. (If you work over a ring, then y + 1 stands for any element and 2y only stands for elements of the ideal generated by 2, etc.) Computations like 0y and y + (-y) both still yield 0. This is not very useful—at least I can't see anything useful to come of it—but, aside from the infectious multi-valuedness, I don't see what problems result.
- globular-toast 7y agoThis was definitely not accidental. Here is the commit that introduces `all` and `any`: https://github.com/python/cpython/commit/96229b19181 https://github.com/python/cpython/commit/96229b19181 It includes a test for the empty case: `self.assertEqual(all([]), True)`. They knew what they were doing.
- qayxc 7y agoOr - and bear with me for this - they wanted to make sure that future changes stay compatible since clients might rely on this behaviour.
- goto11 7y agoWhat do you mean by "Epicycles"? Are you suggesting the code is written like this by random?
- qayxc 7y ago"Epicycles" may refer to ancient astronomers who started with the assumption that Earth is stationary and objects in the sky circle around it. When observed planetary motions were found to be incompatible with the assumption, epicycles were introduced to keep the initial assumption working.
- goto11 7y agoYeah, sorry, I do know what epicycles are. I just don't understand the connection to this issue. It is not like predicate logic was invented to explain the behavior of Python.
- qayxc 7y agoI obviously failed to illustrate the connection the author maybe was referring to: it's an after-the-fact rationalisation, just like epicycles were.
- syphilis2 7y agoI see many complicated explanations for "why all([]) behaves this way" that justify the behavior as good because of logical or mathematical purity. I am implying that these explanations are so long worded because they're wrong. I think they are good reasons for keeping the behavior as-is, but the code was probably written first and was written to be simple and just happens to produce mathematically consistent behavior at an edge case. It would be interesting to see the history of the function, to see if the implementation has changed.
- Demiurge 7y agoPerhaps it happens to check for any false in the list and otherwise returns true. Sometimes mathematically pure is equivalent to simple and vice versa. The explanations are long because they try to explain the implications and make analogies, which could be infinite.