[swift-evolution] [Pitch] Overridable Members in Extensions

Howard Lovatt howard.lovatt at gmail.com
Sun Feb 14 16:12:06 CST 2016

+1 from me. I have wanted this feature to extend a class I don't own.
Equally importantly I find the present semantics confusing.

On Thursday, 11 February 2016, Philippe Hausler via swift-evolution <
swift-evolution at swift.org> wrote:

> Looks really great; this will definitely solve some problems we had with
> the Foundation implementation.
> On Feb 10, 2016, at 5:45 PM, Jordan Rose via swift-evolution <
> swift-evolution at swift.org
> <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');>> wrote:
> Hey, everyone. Here's a small feature with ABI implications, ready for
> feedback. In addition to comments on the proposal itself, I'm also
> interested in hearing how often this comes up for people:
> - extending a class you don't own
> - to add an overridable method
> - where some of the overriders might be outside the current module
> Jordan
> https://github.com/jrose-apple/swift-evolution/blob/overridable-members-in-extensions/proposals/nnnn-overridable-members-in-extensions.md
> ---
> Overridable Members in Extensions
>    - Proposal: SE-NNNN
>    - Author: Jordan Rose <https://github.com/jrose-apple>
>    - Status: *Awaiting review*
>    - Review manager: TBD
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#introduction>
> Introduction
> Today, methods introduced in an extension of a class cannot override or be
> overridden unless the method is (implicitly or explicitly) marked @objc.
> This proposal lifts the blanket restriction while still enforcing safety.
> Note: it's already plan-of-record that if the extension is in the same
> module as the class, the methods will be treated as if they were declared
> in the class itself. This proposal only applies to extensions declared in a
> different module.
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#motivation>
> Motivation
> This is used to add operations to system or library classes that you can
> customize in your own classes, as seen in the Apple AdaptivePhotos
> <https://developer.apple.com/library/ios/samplecode/AdaptivePhotos/Listings/AdaptiveCode_AdaptiveCode_UIViewController_PhotoContents_swift.html> sample
> code.
> extension UIViewController {
>   func containsPhoto(photo: Photo) -> Bool {
>     return false
>   }
> }
> class ConversationViewController : UIViewController {
>   // …
>   override func containsPhoto(photo: Photo) -> Bool {
>     return self.conversation.photos.contains(photo)
>   }
> }
> Additional motivation: parity with Objective-C. If Objective-C didn't
> allow this, we might not have done it, but right now the answer is "if your
> method is ObjC-compatible, just slap an attribute on it; otherwise you're
> out of luck", which isn't really a sound design choice.
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#todays-workaround>Today's
> Workaround
> If you know every class that needs a custom implementation of a method,
> you can use dynamic casts to get the same effect:
> extension UIViewController {
>   final func containsPhoto(photo: Photo) -> Bool {
>     switch self {
>     case is ListTableViewController:
>       return true
>     case let cvc as ConversationViewController:
>       return cvc.conversation.photos.contains(photo)
>     default:
>       return false
>     }
>   }
> }
> But this is not possible if there may be subclasses outside of the module,
> and it either forces all of the implementations into a single method body
> or requires adding dummy methods to each class.
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#proposed-solution>Proposed
> solution
> This proposal lifts the restriction on non- at objc extension methods (and
> properties, and subscripts) by requiring an alternate dispatch mechanism
> that can be arbitrarily extended. To preserve safety and correctness, a
> new, narrower restriction will be put in place:
> *If an extension in module B is extending a class in module A, it may only
> override members added in module B.*
> Any other rule can result in two modules trying to add an override for the
> same method on the same class.
> Note: This rule applies to @objc members as well as non- at objc members.
> There is no restriction on extensions adding new *overridable* members.
> These members can be overridden by any extension in the same module (by the
> above rule) and by a subclass in any module, whether in the class
> declaration itself or in an extension in the same module.
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#detailed-design>Detailed
> design
> Besides safety, the other reason we didn't add this feature is because the
> Swift method dispatch mechanism uses a single virtual dispatch table for a
> class, which cannot be arbitrarily extended after the fact. The
> implementation would require an alternate dispatch mechanism that *can* be
> arbitrarily extended.
> On Apple platforms this is implemented by the Objective-C method table; we
> would provide a simplified implementation of the same on Linux. For a
> selector we would use the mangled name of the original overridden method.
> These methods would still use Swift calling conventions; they're just being
> stored in the same lookup table as Objective-C methods.
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#library-evolution>Library
> Evolution
> As with any other method
> <https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#classes>,
> it is legal to "move" an extension method up to an extension on the base
> class, as long as the original declaration is not removed entirely. The new
> entry point will forward over to the original entry point in order to
> preserve binary compatibility.
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#impact-on-existing-code>Impact
> on existing code
> No existing semantics will be affected. Dispatch for methods in extensions
> may get slower, since it's no longer using a direct call.
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#alternatives-considered>Alternatives
> considered
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#all-extension-methods-are-final>All
> extension methods are final
> This is sound, and doesn't rule out the "closed class hierarchy" partial
> workaround described above. However, it does prevent some reasonable
> patterns that were possible in Objective-C, and it is something we've seen
> developers try to do (with or without @objc).
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#objc-extension-methods-are-overridable-non-objc-methods-are-not>
> @objc extension methods are overridable, non- at objc methods are not
> This is a practical answer, since it requires no further implementation
> work. We could require members in extensions to be explicitly annotated
> dynamic and final, respectively, so that the semantics are at least
> clear. However, it's not a very principled design choice: either
> overridable extension members are useful, or they aren't.
> <https://github.com/jrose-apple/swift-evolution/tree/overridable-members-in-extensions#future-extensions>Future
> extensions
> The restriction that an extension cannot override a method from another
> module is intended for safety purposes, preventing two modules from each
> adding their own override. It's possible to make this a link-time failure
> rather than a compile-time failure by emitting a dummy symbol representing
> the (class, member) pair. Because of this, it may be useful to have an "I
> know what I'm doing" annotation that promises that no one else will add the
> same member; if it does happen then the program will fail to link.
> (Indeed, we probably should do this anyway for @objc overrides, which run
> the risk of run-time collision because of Objective-C language semantics.)
> If we ever have an "SPI" feature that allows public API to be restricted
> to certain clients, it would be reasonable to consider relaxing the safety
> restrictions for those clients specifically on the grounds that the library
> author trusts them to know what they're doing.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');>
> https://lists.swift.org/mailman/listinfo/swift-evolution

-- Howard.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160215/48633d83/attachment.html>

More information about the swift-evolution mailing list