[swift-evolution] Replace the override keyword by 'extend' and 'replace' or add an annotation like @SuppressSuperCall
Howard Lovatt
howard.lovatt at gmail.com
Mon Feb 15 21:36:16 CST 2016
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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160216/e3bd25ac/attachment.html>
More information about the swift-evolution
mailing list