[Refresh Review] SE-0220: count(where:)

Hi Swift community,

The review of SE-0220: count(where:) begins now and runs through July 28, 2023.

This review is a refresh of a previously accepted proposal, which was subsequently expired after it was not shipped within a year of proposal acceptance. The process for proposal expiry is outlined here.

In the particular case of SE-0220, it was found that the introduction of a method Sequence.count(where:) introduced type checking challenges because of ambiguity between it and Collection.count.[1] In the time since the proposal expired, a number of improvements have been made to the type system have resolved this issue, and the language steering group feel the proposal can now be safely implemented in the standard library.

This review refreshes the SE-0220 proposal. The goal of the refresh review is not to re-litigate the original acceptance. Instead, the goals for a refresh review are check, in the time since the previous acceptance, that nothing has significantly changed in the language or ecosystem that now suggests the acceptance is no longer valid.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager. When emailing the review manager directly, please keep the proposal link at the top of the message.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/main/process.md

Thank you,

Ben Cohen
Review Manager


  1. In particular, when combined with the quirk that unapplied functions can drop their argument labels. So ["a"].count could either be a call of the count property getter (an expression of type Int) or a reference to the count(where:) method (an expression of type ((String) throws -> Bool) throws -> Int). In some expressions involving array literals, this ambiguity led to a large impact on type checking time. ↩︎

35 Likes

Per the reduced review criteria I will throw my full support behind the proposal. This is a small QoL improvement that I have reached for several times since it was backed out only to be sadly reminded of its nonexistence.

+1

6 Likes

shutupandtakemymoney.gif

+1 I will be very happy to remove my own implementation of count(where:) from my private library of extensions.

12 Likes

@xwu rightly points out to me that there should be a toolchain available for testing this feature. I will work on getting this available, however in the mean time, you can paste the implemention into your project (which serves almost all needs except, ironically, faithfully reproducing the type checker implications):

extension Sequence {
    @inlinable
    public func count(
        where predicate: (Element) throws -> Bool
    ) rethrows -> Int {
        try self.reduce(0) { n, element in
            if try predicate(element) { n + 1 } else { n }
        }
    }
}
5 Likes

+1, with a huzzah!

My only concern would be regressions to performance, method resolution / ambiguity, or type checking introduced by this feature — and it sound like you’ve already investigated that.

In addition to being a small but substantive quality of life improvement, the re-acceptance of this proposal would stand as a testament to all the excellent work folks have done in the guts of the compiler in recent years.

4 Likes

+1 for Nice Things™!

4 Likes

+1 on the proposal.

Just a small question, is there a reason the function is returning Int instead of UInt?

In swift, the guidelines are to always use Int, even if a number is known to always be non-negative.

1 Like

+1 on the proposal

In swift, the guidelines are to always use Int , even if a number is known to always be non-negative.

For my learning, which guidelines are these?

This is a quote from the Swift language book, "The Swift Programming Language"

Unless you need to work with a specific size of integer, always use Int for integer values in your code. This aids code consistency and interoperability. Even on 32-bit platforms, Int can store any value between -2,147,483,648 and 2,147,483,647 , and is large enough for many integer ranges.

And later in the chapter it says this:

Use UInt only when you specifically need an unsigned integer type with the same size as the platform’s native word size. If this isn’t the case, Int is preferred, even when the values to be stored are known to be nonnegative. A consistent use of Int for integer values aids code interoperability, avoids the need to convert between different number types, and matches integer type inference, as described in Type Safety and Type Inference.

This is a link to that chapter: Documentation
And this is a link to the Swift API design guidelines: Swift.org - API Design Guidelines

8 Likes

Review Conclusion

The review has been accepted.

5 Likes