Extension documentation is incomplete

The documentation for extensions does not fully explain how to use the feature:

  • What is the scope of the extension?
  • Where should the extension be defined?
  • How do extensions interact with each other if there are collisions?
  • Are extensions available outside the current module? If so, can that be limited?
  • Can extensions be applied to only specific instances of an object?

I could go on, but you get the idea. As a newcomer to Swift, I am thoroughly confused about extensions because the documentation does not explain how to actually use the feature, and the examples leave the mechanics as an exercise to the reader, which is not the right way to do it. There needs to be more detail about the implementation.

I'm the last to assert any particular expertise, but hopefully I can answer a few questions about extensions for you

I'm not 100% certain by what you mean by scope here, but I'm guessing you're asking "what can be extended" vs. any scoping as it might pertain to properties, etc. That's laid out at the top of the documentation in the "you can..." section, but basically it lays out you can add functions, computed properties, or adding conformances to a protocol. More notably you can't add stored properties via extension.

Pretty much anywhere - and most notably you can extend a type that's defined in another module that you aren't directly in control of. The extension docs show an example of extending the type Double from the standard library/built-ins to illustrate that.

As a convention that I prefer, I typically create extensions for protocol conformance, and if its a lot of code, I'll drop that into it's own file in my own modules, usually named something like "TypeImExtending+myExtension.swift" - but that's not required anywhere.

You can't nest an extension within a type or protocol - they are expected to be top-level things in your code.

You get a straight up compiler warning - usually "duplicate declaration" complaints.

If you're extending your own type in a library, but don't want that available, you can absolutely make the computed pieces you add have "package", "private", or "file private" access so that it's not exposed beyond the module.

Nope - it's mixing in to the type itself, not an instance.

Is there something you're trying to do? Or I guess put another way, if you're trying to understand "does this work" or "is this idiomatic swift" for some idea of yours, lay it out and we'll be happy to help talk it through.

1 Like

@Joseph_Heck I appreciate the feedback. My goal with this topic is to get the information added to the documentation. After searching for the last few hours, I've seen the same questions dozens - possibly hundreds - of times in various places across the internet. The documentation does not explain how to use the extensions in a practical way. The academic information appears to be sufficient, but that isn't quite enough.

I'm not 100% certain by what you mean by scope here, but I'm guessing you're asking "what can be extended" vs. any scoping as it might pertain to properties, etc.

I am asking how the extensions apply in terms of scope and/or context. Does it impact library code? I am guessing no, but it's just a guess because the documentation doesn't mention this topic in any capacity.

"package", "private", or "file private" access so that it's not exposed beyond the module

Where are the details for this? After reading the new keywords in your comment, I see some explanations like this, but I can't find any of this information on docs.swift.org, which is the only trustworthy source (imo).

Nope - it's mixing in to the type itself, not an instance.
That may seem obvious to those of you are initiated, but it is not obvious to someone with no Swift experience. This kind of thing needs to be explained more explicitly.

How do I get updates posted to the documentation?

You can propose updates to The Swift Programming Language, which is what I think you're aiming for, as it's open source: GitHub - swiftlang/swift-book: The Swift Programming Language book. Read through the top-level README for some of the contribution guidelines - they specifically suggest you add a pitch for explicit changes to talk it through on the forums (within the topic: Swift Documentation - Swift Forums)

1 Like

Extensions, as the term suggests, extend the functionality of existing types. To a first approximation, anything you write inside extension T { ... } behaves as though it were written originally inside the original block declaring the type T.

Extensions have no identity (i.e., they do not "exist") and therefore have no "scope and/or context"—although it's not entirely clear what you mean by that.

As the documentation states explicitly, "If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined." All really means all.

These are access control keywords, and they are covered in the corresponding section of The Swift Programming Language.

In general, the text tries to introduce features in a logical order, but since features (ideally) work together, it is sometimes necessary to read past the first section where a feature is introduced in order to get a full picture of how a feature works.

Access control as it applies to extensions, for example, is discussed in a specific subsection of The Swift Programming Language linked above.

The Swift Programming Language is now open-source, and you can file issues and pull requests against its repository.

1 Like

An extension adds members to some extended type "T", making them available to member name lookup, so if I write "foo.bar" and "foo" is an expression of type "T", then we will look for "bar" in every extension of "T" as well as within "T" itself. This is a purely static, compile-time behavior.

All extensions defined in the current module are always visible, as well as those extensions defined in other modules that you import. This recent proposal clarifies the behavior of extension member visibility with transitive imports: swift-evolution/proposals/0444-member-import-visibility.md at main · swiftlang/swift-evolution · GitHub

At extension can also add a protocol conformance to the extended type:

extension Circle: Shape {...}

This behavior is visible at runtime, in the form of dynamic casts.

As for collisions, the rules are similar to what you'd get if you declared multiple members with the same name inside the type itself. Again, since this is a purely compile-time thing, an extension in an unrelated module cannot mess up existing code by adding new members.

Not specific instances of types, but you can add members to a constrained extension to narrow it down to instances of a generic type with additional requirements:

extension Array where Element: Equatable {
  ...
}

Members of this extension are only visible on an Array of equatable elements, and not any arbitrary array.

4 Likes