[swift-evolution] [Idea] Extend "required" to methods other than init()

Will Entriken fulldecent at gmail.com
Mon Jan 18 10:08:10 CST 2016


Matthew,

This does not address the issue that a class may require all its
subclasses' initializers to call one of its initializers, but does not care
which one.

Are there other languages worth investigating which have the requires super
first/last semantics you note?


On Mon, Jan 18, 2016 at 10:19 AM, Matthew Johnson via swift-evolution <
swift-evolution at swift.org> wrote:

>
> > On Jan 17, 2016, at 5:54 PM, Jesse Squires via swift-evolution <
> swift-evolution at swift.org> wrote:
> >
> > Hey all — this is my first reply to this list, so please forgive me if
> something goes wrong.
> >
> > I wanted to elaborate more on Nate Birkholz's earlier message about
> extending the 'required' keyword to class methods, other than only init.
> >
> > I had suggested this to Joe Groff on Twitter, and encouraged Nate to go
> ahead and start the thread. I’m just now finding the time to respond :) My
> goal here is to flesh out this idea a bit more, and hopefully get a more
> engaging discussion started.
> >
> > Subclassing in Objective-C has the following deficiencies:
> >
> > (1) It lacks the ability to formally declare if a method must be
> overridden.
> >
> > (2) When overriding a method, there is no mechanism to enforce a call to
> super. You could use NS_REQUIRES_SUPER, but not only is this barely
> discoverable, it isn’t part of the language. Further, it merely generates a
> warning when library authors most likely intend for this to be an error.
> (Ok, one could treat warnings as errors, etc. etc.)
> >
> > (3) It is easy for clients to override a superclass method without
> knowing it. That is, subclasses can simply implement an identical selector
> of the superclass without any kind of warning.
> >
> > Clearly this is problematic. What happens when a subclass does not call
> super, but should? I think the only answer is "undefined behavior".
> Typically, the (not so great) solution here is documentation. This is
> common in Cocoa and CocoaTouch, for example each time you (never) read the
> docs, you'll see things like "your implementation must call super at some
> point".
> >
> > Swift improves on these deficiencies in the following ways:
> >
> > (1) For public methods (or internal for classes in the same module),
> Swift requires the 'override' keyword. This is great. Clients are now aware
> that they are overriding a superclass method. Though this *should* be an
> indication to the client that super might need to be called and the docs
> should be read (lol), clients are not required to do either.
> >
> > (2) Superclasses can prevent overriding using the 'final' keyword. This
> provides a lot of safety but results in an "all or nothing" decision for
> library authors.
> >
> > (3) For initializers only, Swift enforces the 'override' keyword *and* a
> call to super due to Swift’s strict initialization rules. Thus, client
> subclasses can safely override initializers. Woo!
> >
> > (4) Additionally, for initializers only, Swift superclasses can require
> that subclasses implement a specific initializer using the 'required'
> keyword (given that a subclass provides a custom init). Again, augmenting
> the subclass initialization flow can be done safely. ::applause::
> >
> > OK. Hopefully I didn’t miss anything there. As you can see, there’s one
> major deficiency remaining in Swift:
> >
> > - If a subclass chooses to override a non-final method of its
> superclass, there is no mechanism to require a call to super.
> >
> > My proposed solution is straight-forward: extend the use of 'required'
> to methods, rather than only allow this for init.
> >
> > Behavior would be the following:
> >
> > - A superclass can specify a method as 'required'.
> > - *If* a client’s subclass chooses to override a 'required' method, it
> must call super.
> > - Without the 'required' keyword, current behavior would remain the same.
>
> Thanks for bringing up this topic.  I agree that we need to support more
> expressive semantics for overrides.  I would take a slightly different
> approach though.
>
> Your use of `required` has a different semantic than its use for
> initializers.  The change is arguably subtly, but it is meaningful.  I
> don’t think it is a good idea for it to have different semantics in
> different contexts.
>
> For initializers `required` means “subclasses must provide an initializer
> with this signature.  The fact that you have to call super just falls out
> of that because it is an initializer.  You don’t actually have to call
> designated initializer that has the matching signature.
>
> You are suggesting making `required` mean “you must call the exact same
> method on super”.  This would be unnecessarily limiting.
>
> There are 3 possible semantics for overridability:
>
> 1) final / not overridable
> 2) overridable
> 3) required
>
> I suggest expanding the use of `required` to mean #3 in general.  I would
> also suggest changing the default to #1.  That would require developers to
> think about subclasses before allowing overrides and also decide whether #2
> or #3 is the correct semantics when overriding is allowed (I know this is
> controversial and somewhat orthogonal to your idea).  This would prompt the
> developer to think about the requirements to call super, which I discuss
> next.
>
> There are also 4 possible semantics for "super requirements” when you do
> override:
>
> 1) no call to super required
> 2) requires super (must call super *if overriden*)
> 3) requires super first
> 4) requires super last
>
> NS_REQUIRES_SUPER is equivalent to #2.
>
> The last two are somewhat subtle, but it is occasionally reasonable for a
> superclass to want its implementation to run before or after a subclass
> implementation.  In both of these cases I would advocate for the compiler
> to automatically synthesize the call to super (if Swift supports these
> semantics) rather than requiring the developer to write the call.  The
> annotation specifies the behavior and the manual call would be redundant.
>
> Swift doesn’t currently have any rules around calling super so we have #1
> as a de-facto default with no way to change that.  That is probably ok as a
> default, especially if we require developers to think about subclasses when
> writing an overridable method (by making final the default).  If we
> introduce syntax to specify 2-4 we would have all cases covered.
>
> Any of the “super requirements” could be specified in conjunction with
> either `overridable` or `required`.  This allows more expressiveness than
> mixing the two concepts together would support.
>
> At minimum, I suggest that you use `required` as mentioned above and
> introduce new syntax to mean “must call the super implementation of the
> same method”.  Adding “super first” and “super last” would be nice as well,
> but can also be added later if you’re not interested in those semantics.
>
> Matthew
>
> >
> > Comments from Joe via twitter:
> > - "Should be straight forward to implement."
> > - "One interesting thing it would enable is invariant implementations of
> covariant protocol requirements." <dogScience.png>
> >
> > Thanks for reading!
> > Jesse
> >
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution at swift.org
> > https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160118/f4b526db/attachment.html>


More information about the swift-evolution mailing list