[swift-evolution] Replace the override keyword by 'extend' and 'replace' or add an annotation like @SuppressSuperCall

Ilya Belenkiy ilya.belenkiy at gmail.com
Tue Feb 16 08:34:08 CST 2016


+1. great original idea. I really like the improvements proposed by Howard.

On Mon, Feb 15, 2016 at 10:36 PM Howard Lovatt via swift-evolution <
swift-evolution at swift.org> wrote:

> Building on Haravikk's suggestion:
>
>     1. Make methods and classes final by default and mark an overridable
> class with `class(option)` or `class(replace)` (see below for
> `class(replace)`).
>     2. A function declaration requires one of `func`, `func(final)`,
> `func(replace)`, `func(option)`, `func(before)`, `func(after)`, or
> `func(instead)` keywords and a function override requires one of
> `override`, `override(final)`, `override(replace)`,
> `override(option)`, `override(before)`, `override(after)`, or
> `override(instead) - note no func keyword.
>     3. A change from Haravikk's suggestion, retain the current meaning of
> override (with different notation) so that `override(option)` means that
> you can call super anywhere or not at all. (Equivalent to current
> `override`.)
>     4. Establish a hierarchy, since the override becomes part of the
> method's public signature, in particular:
>         4.a. `func/override(replace)` can be overridden/implemented by any
> form, i.e. one of `override(replace)`, `override(replace,
> option)`, `override(replace, before)`, `override(replace, after)`,
> and `override(replace, instead)`. (Note the two qualifiers: what you are
> going from to what you are going to.)
>         4.b. `func/override(option)` can be overridden/implemented
> by `override(option)`, `override(option, before)`, `override(option,
> after)`, or `override(option, instead)`.
>         4.c. `func/override(before)`, `func/override(after)`, and
> `func/override(instead)` can only be overridden/implemented by the same
> modifier.
>     5. Methods in protocols and classes that do not have a body are
> automatically `func/override(replace)`. (Currently protocols cannot have
> bodies, but that is likely to change.) Final methods must have a body, i.e.
> `func(final) method()` is an error because it is final and has no body.
>     6. A class with a method without a body, which must be marked
> `func/override` or `func/override(replace)`, has to be marked
> `class(replace)`; it is abstract. (Note the notation `class(option)` and
> `class(replace)` is consistent, a `class(option)` has at least one
> optionally overridable method and no `func/override(replace)` methods,
> whereas a class with at least one `func/override(replace)` method is marked
> `class(replace)`.)
>    7. Any of the annotations can be extended with final, e.g.
> `func(optional, final)` means it is overriding a `func(optional)` but from
> this point down it is final. Final methods must have a body.
>    8. In a class/protocol `func` is shorthand for `func(final)` unless the
> method has no body in which case it is shorthand for `func(replace)`.
>    9. `override` is a shorthand for `override(A, final)` where A is the
> annotation specified in the matching `func` declaration.
>    10. Overriding methods in a final class do not require the extra final
> annotation, e.g. in a final class `func(option)` and `func(option, final)`
> are equivalent.
>
> EG:
>
>     class(replace) Abstract { // Point 1: class marked as replace, it is
> therefore abstract and you can't call init. Point 6: it has an abstract
> method therefore must be abstract itself.
>         func abstract() // Point 5: declare an abstract method (it has no
> body), equivalent to `func(replace)`.
>
>         func(option) overridable() { // Point 2: declare an optionally
> overridable method. Point 3: same override semantics as Swift 2.
>             print("Optionally overridable with any super call OK")
>         }
>
>         func finalFunc() { // Point 1: methods are final by default. Point
> 8: `func` is shorthand for `func(final)` when the method has a body.
>             print("Method is final")
>         }
>     }
>
>     class Final: Overridable { // Point 1: class is final since it isn't
> annotated.
>         override abstract() { // Point 2: override keyword not func. Point
> 7: method is final because class is final. Point 9: `override` is
> equivalent to `override(replace, final)`.
>             print("Implementation of abstract method")
>         }
>
>         override overridable() { // Point 2: override keyword not func.
> Point 7: method is final because class is final. Point 9: `override` is
> equivalent to `override(option, final)`.
>             print("Method is final because class is final")
>         }
>     }
>
>     class(option) Derived: Overridable { // Point 1: class is overridable
> because it is annotated option.
>         override(replace, after) abstract() { // Point 4.a: implementation
> of an abstract class
>             print("In a derived class this will be printed after the
> derived class's body has finished")
>         }
>
>         override(option, final) overridable() { // Point 7: method is
> final because it is annotated final.
>             print("Method is final because class is final")
>         }
>     }
>
>
>   -- Howard.
>
> On 16 February 2016 at 09:56, Haravikk via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> Oh, it looks like Alexey beat me to a similar solution, but split the
>> thread (at least in Mail.app):
>>
>> On Mon, Feb 15, 2016 at 2:07 PM Alexey Demedetskiy via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> Hi
>>
>> I would like to suggest you to extend your proposal.
>>
>> In my practice, overriding super functions can have several semantics.
>> 1) Replace - simple case for abstract classes which implementation do
>> nothing, or throw an exceptions.
>> 2) After super - things like viewDidLoad and viewWillAppear, setUp etc.
>> All cases where super expect to be called before child code.
>> 3) Before super - opposite to 2.
>> 4) Override - no rules about order, but super call must be done.
>>
>> So code can look like:
>>
>> override(after) func viewDidLoad() {
>>    // super.viewDidLoad() <— no need to call super at first line.
>>    // child code
>> }
>>
>> override(before) func tearDown() {
>>    // clean code
>>    // super… inserted by compiler
>> }
>>
>> override(instead) func loadView() {
>>    // super.loadView() <— marked as an error with appropriate fix-up to
>> remove instead modifier
>> }
>>
>> override func refillHealthBar() {
>>   // absent call to super will cause an error with fix-up to add
>> (instead) modifier
>> }
>>
>> I am not sure about exposing this in a public interface and limit child
>> override options.
>>
>> But in general - what is your thoughts about this approach to problem
>> that you mention?
>>
>>
>> On 15 Feb 2016, at 22:52, Haravikk via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> This is an interesting idea, and fits well with the theme of preventing
>> mistakes in Swift, but I think that the proposed solution isn’t flexible
>> enough, as there are cases for inheritance patterns where extending doesn’t
>> actually make sense, so having to specify an exception every time could
>> quickly become annoying.
>>
>> I think the better solution is to instead allow super-classes to specify
>> whether or not their method must be called when overridden/extended. For
>> example:
>>
>> class View {
>> @super(required) func viewDidLoad() { … }
>> }
>>
>> class Button : View {
>> override func viewDidLoad() { … }
>> }
>>
>> class Widget : View {
>> override func viewDidLoad() {
>> super.viewDidLoad()
>>>> }
>> }
>>
>> In this extension of your example Button will cause an error because it
>> overrides viewDidLoad() but fails to call the parent’s method as required
>> by the @super attribute. However, Widget compiles successfully because it
>> follows the requirement.
>>
>> So the options for @super would be:
>>
>>
>>    - *required: *child-class must call parent implementation of this
>>    method.
>>    - *optional:* default behaviour, child can choose whether to call the
>>    parent’s method.
>>    - *denied:* child may not call parent’s method (useful if it makes
>>    assumptions that a child-class may not follow), but can still
>>    extend/override.
>>
>>
>> I think this would be a more flexible solution to the problem, and put
>> the decision in the hands of those writing classes designed for
>> inheritance. I’m not 100% sure of whether to rename override to extend, I
>> like extend better personally, but it probably doesn’t matter overall.
>>
>> On 15 Feb 2016, at 20:57, Florian Liefers via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> Hi!
>>
>> I would like to suggest to replace the override keyword for functions by
>> something like extend and replace or to add an annotation like
>> @SuppressSuperCall (don’t know a good name for it).
>> The reason for this is, that it might happen, that one forgets to call
>> the super’s implementation in an overridden function or if one reads the
>> code it might not be obvious why the super’s implementation is not called:
>>
>> class View {
>>   func viewDidLoad() {
>>      // does something
>>   }
>> }
>>
>> class Button: View {
>>  override func viewDidLoad() {
>>      super.viewDidLoad()   // <— this might be forgotten
>>      // do something other
>>   }
>> }
>>
>> The compiler will accept if one overrides a superclass’s function but
>> does not call the superclass’s implementation which is often ok. The
>> developer should clearly state that he doesn’t want to call the
>> superclass’s implementation, otherwise the compiler should throw an error.
>>
>> // Example for extending a function
>> class Button: View {
>>  extend func viewDidLoad() {
>>      super.viewDidLoad()
>>      // do something
>>   }
>>
>>  extend func viewDidAppear() {
>>      // do something
>>   } // <— the compiler should throw an error here.
>> }
>>
>> // Example for replacing a function
>> class Geometry {
>>   func volume() -> Double {
>>      return 0;
>>   }
>> }
>>
>> class Cube: Geometry {
>>   var length: Double = 0.0
>>   replace func volume() -> Double {
>>      let v = length * length * length
>>      return v
>>   }
>> }
>>
>> Cheers,
>> Florian
>> _______________________________________________
>> 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
>>
>>
>>
>> _______________________________________________
>> 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/20160216/53a02080/attachment.html>


More information about the swift-evolution mailing list