> 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.
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.
Garbage collectors are used in most modern languages. Most implementations of garbage collectors don't give any real time guarantees. And neither do typical free/malloc implementations.
You can do functional programming without garbage collectors. Eg via linear types / uniqueness typing.
But the more general answer here is: whether your tool is appropriate depends on your problem.
Hard real time constraints are rare for most software. Most languages in widespread use don't support those out-of-the-box. Eg C certainly doesn't.
> It 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.
You know that eg Haskell allows you to write imperative code just fine? It's actually one of the more pleasant languages for imperative programming.
And it's pretty easy to get proponents of FP to admit that imperative programming has its uses. Much easier at least, than to get them to say positive things about Java-style OOP.
> 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.
As I said most functional languages support imperative programming styles just fine. But, of course, your complaint remains more valid if you are redirecting it against certain functional programmers. Languages are tools to be used by people. Some of them more flexible, some more boneheaded.
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-wor...
I might as well generalize my statement to:
"Programming languages tend to elevate some ideals before concerns of practical safety, performance or just common sense."
It just wouldn't make as much sense in-context.
That statement would be difficult to substantiate, particularly in the context of functional languages. Practical safety, performance, and common sense are absolutely priorities; the ideals that functional language design follows are servants of those goals, not the master.
all([True, True] + []) is equivalent to `True and True and ?`. If you replace ? with False then all() will never return True in any case because given any list you can always create an equivalent list by appending [] which would taint the list to always return False on all(). Obviously nobody wants all() to return False on a valid list and the concept of tainting a list is pretty stupid so True is the only option. If you replace ? with True you can add as many empty lists as you like which is the desired behavior.
all([True, True] + [] + [] + []) == True and True and True and True and True
If it was obvious from any standpoint, we obviously wouldn't be having this blog post or this discussion thread.
Natural language - the natural way to think about things - does not always intuitively translate over to predicate logic. If you disagree with that, you should try teaching it to a class of high schoolers.
If you think of "all" as "in this set, no element exists which is False" then it is "obviously" the right behavior.
However, consider this more practical example:
x = {1,2,3}
y = {}
all(e in x for e in y)
> True
This doesn't strike me as obviously right.We invented mathematics because practical thinking leaves us without answers at some points. You are free to axiomatically choose "all([]) = false", but then you'll find out that many mathematical equivalences won't hold as they all assume only the ZF axioms.
If it did look right to me, I wouldn't be sitting here typing this all out. I would be silently agreeing with the mathematical point of view.
That's what tells me this is a likely source of confusion and error.
Is there any language that works the way you’re suggesting is more practical/intuitive?
For practical reasons, it might be better for it to be false. That would require some empirical research on whether this could actually prevent harmful behavior in practice.
From a design perspective, I would argue that "all" should not be defined for empty lists, because that forces people to consider whether the often-forgotten "empty list" edge case might cause an issue.
The consequence of "if (True)" is usually to do something, not nothing. The consequence of doing something by overlooking the "empty list" special case may be harmful.
Preventing potential harm is more important than not having the programmer jump through a couple of extra hoops to account for the special case. That makes the special case impossible to overlook.
This kind of reasoning is generally accepted when talking about implicitly nullable types vs explicitly nullable types (optional types), but somehow in this context people are more stubborn.
Not sure that's the same? Haskell has no implicitly nullable types. Everything not explicitly marked as optional is non-nullable.
But still, if your algorithm at hand can treat nulls the same as non-nulls, it's a good idea to do so. (Eg in a sorting algorithm where you just feed the elements to the user supplied comparison function, and let that function handle comparing of optional elements.)
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.