+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@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@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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution