Swift Regret: Open Protocols

Part of the Swift Regrets series.

Sometimes you have a protocol that only makes sense for types in your module to conform to, but that you want to have as a generic constraint. “PropertyList” is a good example. Someone else cannot just make a new plist type.

I’ll say up front: This is an Adrian M appreciation post, because they brought up this need back during the controversial SE-0117 discussions, which first made the public/open distinction for classes. Because protocols already separate their customization points from their fixed extension methods, the primary, method-level motivation for open didn’t apply. And SE-0117 was already a battleground. So I and others pushed back on Adrian, saying we needed to focus on classes.

But—the way these things tend to go—afterwards I kept seeing places where it would be useful. Where it would better communicate the library author’s intent, or allow them to add a new requirement without a pointless default implementation. Not often, but sometimes.

Now, there is a tool that works in a closed set of types: an enum. But enums don’t do everything protocols do: you can’t have associated types that differ for each case, and you can’t have a case that can hold any Set<T>.1 And there’s no implicit conversion for ease of use. Meanwhile, non-open protocols have precedent in Scala and Java (and Kotlin), so it’s not totally new ground.

This isn’t a major regret because it’s niche, and documentable. I also think we’ll someday have the impetus to add non-public requirements to public protocols, which has the same effect. (Types outside the module can never satisfy that requirement.) But even if we do that, it’ll still be different from the simple thing we have with classes, and the wrong default for library authors, for no good reason.

…unless we change the default in a -swift-version, I suppose. But that’s a big ask.

P.S. For those worried about testing, remember that testable imports allow subclassing non-open classes, and the same would be true for non-open protocols.

  1. Aside: there are languages that support something like case items<T>(Set<T>), a feature called Generalized Algebraic Datatypes, or GADTs. We could add this to Swift, but that’s a whole separate design and proposal and implementation. ↩︎