Hi Swift Evolution.
A short pitch that is part of preparing the std lib for ABI stability, as well as readying for move-only types in the future. GitHub version here
Remove Some Customization Points from the Standard Library's
- Proposal: SE-NNNN
- Author: Ben Cohen
- Review Manager: TBD
- Status: Awaiting implementation
- Implementation: apple/swift#19769
This proposal removes four customization points from protocols in the standard library:
Customization points are methods that are declared on the protocol, as well as given default implementations in an extension. This way, when a type provides its own non-default implementation, this will be dispatched to in a generic context (e.g. when another method defined in an extension on the protocol calls the cusomized method). Without a customization point, the default implementation is called in the generic context.
This serves broadly two purposes:
Allowing for differences in behavior. For example, an "add element" method on
a set type might exclude duplicates while on a bag it might allow them.
Allowing for more efficient implementations. For example,
forward-only collections takes O(n) (because without random access, the
implementation needs to iterate the collection to count it). But some
collection types might know their
counteven if they aren't random accesss.
Once ABI stability has been declared for a framework, customization points can never be removed, though they can be added.
Customization points aren't free – they add a small cost at both compile time and run time. So they should only be added if there is a realistic possibility that one of the two reasons above are necessary.
In the case of the 4 customization points in this proposal, reason 1 does not apply. In fact it could be considered a serious bug if any type implemented these 4 features with anything other than the default obvervable behavior.
It is also hard to find a good use case for reason 2 – whereas slight slowdowns from the presence of the customization points have been observed. While it is possible that a resilient type's
forEach implementation might be able to eek out a small performance benefit (for example, to avoid the reference count bump
of putting self into an iterator), it is generally harmful to encourage this kind of "maybe forEach could be faster" micro-optimization. For example, see here, where error control flow was used in order to break out of the
forEach early, causing unpleasant interference for debugging workflows that detected when errors were thrown.
Future move-only type considerations
In the case of
last there is an additional consideration: in the future, collections of move-only types (including
Array) will not be able to reasonably fulfil these requirements.
A collection that contains move-only types will only allow elements to be either removed and returned (e.g. with
popLast()), or borrowed (e.g. via
Returning an optional to represent the first element fits into neither of these buckets. You cannot write a generic implementation of
first that fetches the first move-only element of a collection using a subscript, moves it into an optional, and then returns that optional.
last need to be removed as requirements on the collection protocols in order to make it possible for collections of move only types to conform to them.
They would remain on
Collection via extensions. When move-only types are introduced, those extensions will be constrained to the collection element being copyable.
Once the final functionality for move-only types is designed, it may be that language features will be added that allow for borrowing into an optional, allowing even collections of move-only types to implement a
But it's better to err on the side of caution for now and remove them from the protocol.
Remove these 4 customization points from the
Collection protocols. The default implementations will remain.
These are customization points with an existing default implementation, so there is no effect on source stability.
It is theoretically possible that removing these customization points could result in a behavior change on types that rely on the dynamic dispatch to add additional logic. However, this would be an extremely dubious practice e.g.
MyCollection.first should really never do anything more than return the first element.
Effect on ABI stability
Removing customization points is not an ABI-stable operation. The driver for this proposal is to do this before declaring ABI stability.
Effect on API resilience
Leave them in. Live with the slight code/performance impact in the case of
forEach, and work around the issue when designing move-only types.