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

Matthew Johnson matthew at anandabits.com
Wed Jan 20 20:39:19 CST 2016


> On Jan 20, 2016, at 6:53 PM, Jesse Squires <jesse.d.squires at gmail.com> wrote:
> 
> Thanks Matthew and Howard! A lot of good points there.
> 
> There are certainly a few more possible semantics for overrides than I was initially thinking. It’s not clear to me if the language should support each use case or not. Doing so seems to add unnecessary complexity for (potentially) little gain. 
> 
> I can’t say for sure, but my guess is that the *most common* scenario is: "if a method is overridden, it must call super first". This would also make automatically synthesizing the call to super simpler, which I think is a great idea. Clients then have less of a burden when subclassing.
> However, as noted, non-void returns complicate this. Also, I agree that re-purposing `required` could be confusing. I’m open to ideas for an alternative keyword.
> 
> Again, a lot of good ideas/feedback. I’m actually not sure where this leaves us. :) Accounting for all of the possible semantics seems too complex and burdensome, though I think our current state is insufficient. And perhaps accounting for only the simplest scenario, "if a method is overridden, it must call super first", would only further highlight the lack of ability to express the other possible semantics.
> 
> ¯\_(ツ)_/¯  I’d love to hear if anyone else has ideas or shares these concerns! 

Are all of the possible semantics a bit complex and perhaps subtle in some cases?  You can argue that.  However, they do exist *in practice* whether they can be expressed in the language or not.  We currently rely on documentation to communicate the semantics that subclasses must adhere to in order to function properly.  

Personally, I consider it a flaw in the language that we must rely on documentation for this kind of thing if it is considered an acceptable requirement to place on subclasses.  It would be much better to express directly and have it enforced by the compiler.  I don’t believe it would be correct to consider this “complex and burdensome” in the language.  The complexity is inherent in the design of the superclass.  The question is how that complexity is communicated to developers and whether or not mistakes are caught at compile time.

At the same time, I understand the desire to keep the language simple.  Assuming that is the direction you want to go my recommendation is:

1. Allow `required` to be used on any method, indicating that all subclasses must implement the method (matching its semantics on initializers).
2. Add a new annotation indicating that overrides must call super (maybe @requires_super, but could be something else).

We can always add “first” and “last” parameters on that annotation - @requires_super(first) and @requires_super(last) - as well as an annotation along the lines of @no_super down the road.

-Matthew

> 
> Jesse
> 
> 
>> On Jan 18, 2016, at 11:34 AM, Howard Lovatt via swift-evolution <swift-evolution at swift.org> wrote:
>> 
>>>> 
>>>> Are there other languages worth investigating which have the requires super first/last semantics you note?
>>> 
>>> I don’t know of any languages that support this.  It is not the most common semantic, but when it occurs it seems important enough that it would be better if it could be specified in the language itself and enforced rather than left to documentation.
>> 
>> BETA has this ability, but it worked rather differently than modern OO languages (which work more like its predecessor Simula). In BETA you can only ever call the top most method, it can then optionally call the immediately overriding method. The syntax BETA uses is `inner(<args>)`. Probably easier with examples (in BETArised Swift):
>> 
>>   class Base {
>>       func finalM() { print("finalM") }
>>       func mustOverrideM() {
>>           print("beforeInner")
>>           inner()
>>           print("afterInner")
>>       }
>>   }
>> 
>>   class Derived: Base {
>>       func mustOverrideM() {
>>           print("inInner")
>>       }
>>   }
>> 
>> Methods `finalM` and `Derived.mustOverrideM` are automatically final because they do not call inner. 
>> 
>> You can't make an instance of `Base` because method `Base.mustOverrideM` needs to be overridden, i.e. `Base` is automatically abstract. 
>> 
>> If you:
>> 
>>   let d = Derived()
>>   d.mustBeOverriddenM()
>> 
>> Then it prints:
>> 
>>   beforeInner
>>   inInner
>>   afterInner
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 



More information about the swift-evolution mailing list