Is Redeclaration of Method/Property of Imported Type Allowed via Extension?!?

Is it valid to import a type from a library or framework, and then redeclare one of its methods or computed properties via an extension of the type?

For example:

import Foundation
extension Date { var description: String { "not a date" } } // redeclaration error not emitted
print(Date().description) // not a date
print(Date()) // 2020-06-08 07:29:24 +0000

Note that, internally, the imported framework/library continues to call it own version of the method/property. Or, so it appears.

Why doesn't the compiler emit an error?

1 Like

Is it valid to import a type from a library or framework, and then
redeclare one of its methods or computed properties via an extension
of the type?

You need to clarify what you mean by “valid”. It certains compiles and has well-defined and deliberately-chosen semantics. So I suspect you’re asking:

  • Is it good form?

  • Should we change this behaviour?

IMO the answers here are “No.” and “That’s a question for Swift Evolution.”, respectively (-:

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

It's inherent to the logic of a language that allows users to extend other people's types. Otherwise, API authors could never add new methods to their own types without potentially stopping their users' code from compiling.

Xiaodi, thank you.

I do not doubt what you've written. But, please indulge me for a moment, as I'm exploring this behavior in the larger context of trying to understand protocol conformance behaviors.

Are you aware of any formal documentation of this overriding behavior? The only documentation that I've found is this note in the introduction to the Extension chapter of The Swift Programming Language (maybe I've not been diligent in my search):

NOTE
Extensions can add new functionality to a type, but they cannot override existing functionality.

I would've thought that that note was speaking to this situation, among others. [I also observe that, in light of the ability to provide conditional conformances in extensions, that note is not entirely accurate.]

From a distance, I've assumed the following about the interrelationship between modules vis a vis types:

(A) VENDORS POV. The burden would be on the vendor of a module, first and foremost, (1) to not engage in the practice of publicly extending types that originate outside their module; (2) when they break rule (1), to prominently and clearly document it; and (3) when they alter the API of types that originate in their module, to prominently and clearly document it.

(B) VENDEE'S POV. The burden would be on the vendee of a module, first and foremost, (1) if extending a type originating in the module, to take care to not choose member names that conflict, or are likely to conflict, with the type's module-provided API, and (2) just in case the vendor might change the type's API in a way that conflicts with the vendee's extension, to be fully prepared to update the vendee's program to adjust to the API change. I observe that doing (2) is really no different than the vigilance and effort required to use any module outside of one's own control and that the need to do (2) it is totally avoidable.

(C) EDGE AND CORNER CASES. In the (I think) unusual case of two modules both extending the same type with the same member name and then publicly vending that type (e.g., modules A and B both extend Array<T> where T: Comparable to provide an isOrdered property), with a third module importing both of those modules and using the conflicting type, I suppose the owner of the third module could deal with the conflict by never calling the conflicting member, and instead creating some different member name to provide the same functionality.

Am I missing issues and/or inappropriately minimizing the ones that I've addressed? Are you aware of a post or other source on the underlying principles that I might consult?

You're not missing anything, except a bit of history.

In Obj-C, it has always been a huge problem that a 3rd party subclass of a system framework class (e.g. UIViewController) could accidentally override a method name in the framework.

The nightmare scenario was not overriding a method name that was visible at the time the override was written, or even using a method name that conflicted with a private (invisible) method inside the framework, but using a method name that was later used for a new private method in the framework.

When that happens (and it did), the 3rd party app breaks, and it's not really anyone's fault. Of course, once this happened to you, you started choosing method names defensively in an override, but the bad outcome was disproportionately bad for less experienced programmers.

(Eventually, I can't remember exactly when, maybe around macOS 10.4 or 10.5, Apple introduced a convention that its own private methods would start with an underscore, and 3rd party methods should not start with an underscore. However, the problem still lurked.)

For Swift, the intention was always to solve this problem by making method names "private" to their visibility. That means that private and fileprivate method names are qualified by their filename to make them unique, and internal method names are qualified … somehow else … to prevent them from conflicting with method names in other modules.

In addition, Swift was designed to require an override keyword to detect the related case of a framework introducing a new public method that conflicted with a non-override method of the same name already existing in a 3rd party subclass. This produces an error message, because there's no way to scope this automatically.

Quincey, thank you very much!

After reviewing the history you describe and reading more about the topic elsewhere, it is not clear to me whether this feature is still necessary and advisable as part of Swift.