We *can* write parameterized extensions, but… should we?

i used to never write extensions on external protocols. but i eventually came around to adopting the practice, and in hindsight this was really because of tooling limitations: for a long time on linux we did not have LSP, and it is really hard to use extensions on external protocols without LSP support.

but today we have swift on VSCode with modern sourcekit-lsp features, and it is “reasonably” stable now, so i write extensions on standard library protocols now.

with SE-0631, extensions on types like Optional where the generic parameter is bound to a non-generic type read a lot like extensions on the wrapped type itself, so nowadays i think the readability of this pattern is excellent:

extension MyType?
{
}

extension [MyElement]
{
}

extension [MyKey: MyValue]
{
}

extension Sequence<MyElement>
{
}

the discoverability of these kinds of methods is still poor, because their documentation all gets pooled together in one giant slush pile on Optional, Array, Dictionary, Sequence, etc, but the excellent readability means it’s easy to skim a pageful of Optional members and find the API i am looking for.

but parameterized extensions still have awful readability:

extension Optional
{
    @inlinable public mutating
    func revert<Instant, Value>(to rollbacks:GenericType<Instant, Value>.Rollbacks)
        where Wrapped == GenericType<Instant, Value>.Head
    {
        if let head:GenericType<Instant, Value>.Head = self
        {
            self = rollbacks[head]
        }
    }
}

for an internal or below method, i wouldn’t care, because the API fits more naturally as an extension on Optional. but because the API is public, i end up refactoring it into something like

extension GenericType.Rollbacks
{
    @inlinable public
    func revert(_ optional:inout GenericType<Instant, Value>.Head?)
    {
        if let head:GenericType<Instant, Value>.Head = optional
        {
            optional = self[head]
        }
    }
}

so that it doesn’t get lost in the pile of Optional members. but this can be a lot of work, and increases the barrier to splitting up a large module into smaller modules.

thoughts?

I remember that the issue of wrong autocompletion when dealing with extensions on generic types with type parameters constrained to be generic types themselves (a.k.a. "simulated parameterized extensions") was brought up several times before in the forums, and it was considered a bug by the core team (I can't find the posts though).

In the latest Xcode (14.0.1), this is still a bug:

The autocompletion should not suggest unbox() for the y instance.