When you use allSatisfy function in Array, Dictionary...
You have to check empty.
Because if Array, Dictionary are empty, results of allSatisfy are true.
let numbers = [Int]()
let numbers2 = [1,2,3,4,5]
func isGreaterThanThree(_ number: Int) -> Bool {
return number > 3
}
let numbersAreGreaterThanThree = numbers.allSatisfy(isGreaterThanThree) // true
let numbers2AreGreaterThanThree = numbers2.allSatisfy(isGreaterThanThree) // false
// So we must check empty.
func isNumbersAreGreaterThanThree() -> Bool {
guard numbers.isEmpty == false else {
return false
}
numbers.allSatisfy(isGreaterThanThree)
}
Motivation
When you use reduce function, you can define initial result.
So I think that if we can define empty result, we don't have to check empty.
allSatisfy is just a universal qualifier from first-order logic, which evaluates to true for empty collection.
I don't immediately see what kind of logic needs this special treatment. And treating empty collection the same way you treat collection with non-satisfying element could easily be harmful.
@Lantua
Description of retun value written in document like that.
Return Value
true if the sequence contains only elements that satisfy predicate; otherwise, false.
I can't see anywhere that allSatisfy evaluates to true for empty collection in document.
I think empty collection can't satify predicate. So result of allSatisfy with empty collection have to false.
But, allSatisfy has already release. So I suggest make one more allSatisfy function that has parameter for empty.
There is more detail example
// Async example
var data = [String: Data]()
var requestURLS = [String: URL]() // requestURL must bigger than 0
requestURLS.forEach { keyValue in
// async function
func request(keyValue.value, completion: { [weak self] data in
self?.data[keyValue.key] = data
self?.jobFinish
}
}
func jobFinish() {
guard self.data.isEmpty == false else {
print("job is not finished")
return
}
if self.data.allSatisfy { self.requestURLS.allKeys.contains($0.key) } {
print("job is finished")
} else {
print("job is not finished")
}
}
And I edited title to explain my idea more correctly
The collection does not contain any element which fails the predicate. Therefore the collection contains only elements which satisfy the predicate.
There are no counterexamples. You cannot find an element of the collection that doesnât satisfy the predicate. Every element of the collection satisfies the predicate.
Technically true, but the logic is a little contorted. It would be reasonable and more direct to say, based on the documentation, that the case of the empty collection has undefined behaviour.
The problem is the language used in Appleâs documentation implies the precondition that the collection contains some elements. Itâs not unreasonable for a native English speaker to be confused given the ambiguous wording, let-alone the majority of the planetâs population that arenât native English speakers.
Clearly the behaviour has been decided, so the only practical course left is to improve the phrasing of the methodâs documentation. Whether itâs also useful to introduce a variant or successor with definable behaviour for an empty collection⌠Iâm not sure. It seems like the problem is more with the confusion, than the need to write if not collection.isEmpty and ⌠first.
The related method contains(where:) doesnât return true if the collection is empty. Thatâs another source of potential confusion, since one could expect that the logical âanyâ and âallâ functions would return the same value on an empty collection, being a âspecial caseâ.
(Yes, I know that one can rationalise the current behaviour also - thatâs not the point; the point is that thereâs room for confusion)
While I agree that there is room for improvement in the documentation regarding phrasing (maybe adding a few simple code examples too?), I also think that the current behavior makes sense and I'm not sure if an overload to specify a default for empty collection is really needed. I do, however have no objections to having it because even though I have never come across this need myself I do see the use of it.
I understand that this can be confusing to people not accustomed to mathematical thinking, but the current behaviour agrees with how mathematics and logic think about empty collections (it's a so called "vacuous implication", forall x. is_in_list(x) -> predicate(x)).
Maybe it's just my style, but I would have if let foo = NonEmptyArray(array) higher up rather than have code that needs to do contigency handling later if something is empty.
Functionally, yes. This is a style thing as much as anything, I think. Like @Max_Howell1, I kind of prefer to use precise typing - and coercions or conversions between them as necessary - rather than assumptions about state. Itâs technically safer, if nothing else - I can forget (or screw up) a guard statement, but I canât compile code if I miss a necessary type conversion.
I personally donât think the difference is big enough to try changing Swiftâs established patterns on this, but I can understand how others can be a bit uncomfortable with it.
I also think thereâs better ways to handle this, ultimately - e.g. add constraints to methods and have the compiler infer state from leading statements & conditionals. e.g. allSatisfy could require that the collection not be empty, but Swift would understand that this is guaranteed by a correct guard statement (or similar control flow statement or preceding constraint), and everything would be fine. (minding that allSatisfy isnât a great example since itâs far from unequivocal that it shouldnât handle empty collections, but you get the concept)
This is exactly what we don't want. allSatisfy has a complete specification today for any Collection, there's no reason to change that to restrict it to being a partial function on Collection.
It was weird for me at first too, but itâs not a bug. Look at it this way: allSatisfy doesnât look for 100% matching, but 0% failure. Thatâs why it returns true for empty sequences.
I think I read some other thread on this, and the logic table on two collections possibly satisfying the method and whether the fusion does is only consistent when an empty sequence returns true.
I think this - and other comments about the mathematical conventions & validity of the current behaviour - continue to miss the point: the documentation could simply be clearer, and it's reasonable to suggest making this behaviour configurable since there clearly are valid use cases for both behaviours. The only question on the latter point is if it's justified vs the existing, alternative approach of explicitly checking isEmpty.
I think that if folks can focus on those two points, this thread will be more productive.
There seem to be two different conversations happening here.
can the documentation be clearer?
(from the OP) can we avoid needing to check for empty separately from calling allSatisfy()?
My opinion is yes to (1) and no to (2). More specifically, it would be incorrect to overload allSatisfy() with a function of different semantics or to assume semantics other than those inherited from logic. It may not seem intuitive to some that zero elements can âall satisfyâ a predicate but itâs no more unusual than counting the elements of an array and getting 0 as the answer instead of the count function erroring out or having an overload that allows you to say âcount these things IF they number more than 0.â Thereâs no reason individual code bases couldnât provide conveniences for themselves such as a function (whatever you call it) like nonEmptyAndAllSatisfy().
[EDIT] In fact, leaving these types of conveniences out of the standard library leaves the strategy for handling the OPs situation open to different solutions depending on your taste; I personally would use a NonEmpty type like mentioned up thread in order to move the predicate that the collection is ânot emptyâ As close as possible to the definition of the value, thus reducing the number of times it must be checked for emptiness.
This piece isn't a SE discussion, it's just a PR against the stdlib, since it's uncontroversial and has neither API nor compatibility implications.
For all the reasons discussed upthread, and especially in @mattpolzin's response, this would not be a desirable change.
There's a third piece, which is "what would a more idiomatic way to write this be?"--that's by far the most interesting of the three, but it's also outside the scope of Evolution and would be a better fit for Using Swift.