[swift-evolution] Unify `static` and `class` keywords

Jordan Rose jordan_rose at apple.com
Thu Dec 10 12:17:35 CST 2015


The current behavior is not a bug; it matches the behavior for instance computed properties. To put it another way, if the base class's computed implementation stored values in a dictionary keyed by 'self', it would effectively be an implementation of true class properties. (People do indeed do this in Objective-C, although it's not common.)

We definitely don't want setting a base class property to invoke an arbitrary number of arbitrarily-ordered observers. Moreover, what if some of the subclasses actually override the setter entirely, instead of just adding an observer? That's clearly not implementable.

Again, I'm not saying you/we can't pick a behavior and stick with it. I just want the tradeoffs to be clear.

Jordan


> On Dec 10, 2015, at 7:23, Ling Wang <an00na at gmail.com> wrote:
> 
> When verifying my class object modal I actually found one issue of the current class property observers.
> 
> In the current implementation, when super class’s class property setter is called, subclasses’ observers don’t get called. But after the call of super setter, the value of this property accessed via subclasses(suppose them don’t overload get/set) does change. It means their properties are changed without their knowing it even though they explicitly define observers to observe the changes.
> 
> ```
> var backingVarForFooI = 0
> 
> class Foo {
>     class var i: Int {
>         get {
>             return backingVarForFooI
>         }
>         
>         set {
>             backingVarForFooI = newValue
>             print("Foo.i.set \(newValue)")
>         }
>     }
> }
> 
> class Bar: Foo {
>     override class var i: Int {
>         didSet {
>             print("Bar.i.didSet \(i)")
>         }
>     }
> }
> 
> print(Bar.i)
> Foo.i = 3
> print(Bar.i)
> ```
> 
> If we think about it, it doesn’t matter whether the proper is stored or calculated. Storage is just the implementation details, it doesn’t affect the interface or the concept. Besides, even calculated properties need storage if they are mutable.
> 
> After super class changes a property, people expect to get the changed value when they access the property via their subclasses(suppose they don’t overload get/set) and expect the corresponding observers to get called. So the class object modal that I speculated sharing properties among all subclasses and super class and notifying them about property changes is the only sound modal in this aspect. So the concern about overloading stored type properties with observers is not only about stored type properties but about all type properties.
> 
> So there is nothing special to separate stored type properties from calculated type properties. It supports my proposal to unite them under a more general keyword.
> 
> - Ling
> 
>> On Dec 9, 2015, at 10:23 AM, Ling Wang <an00na at gmail.com <mailto:an00na at gmail.com>> wrote:
>> 
>> This is a legit concern that I didn’t consider before.
>> 
>> I think there are several options, from the most general to the most subtle:
>> 1. Stored type properties are implicit `final`, so behave exactly the same as the current static stored properties.
>> 2. Stored type vars are implicit `final`, as you suggested, so behave exactly the same as the current static stored vars. All other type properties(including stored type lets) are normally overridable.
>> 3. Store type vars are overridable but can not be observed. 
>> 
>> There could be a even more subtle option if my understanding of how classes exist in runtime is correct.
>> 
>> In my understanding, classes exist as singleton instances of their meta classes. If it is true, which I believe is the image in most people’s heads if they know about meta classes, then the class objects tree is exactly the same as the class hierarchy tree. Every subclass object contains(or points to) its super class object in its layout. The super class part is shared by all subclass objects and the super class object per se. So stored type properties are conceptually and naturally shared by the super class and all its subclasses, which behave exactly like the current static stored properties.
>> 
>> In this modal, the 4th option is:
>> 4. Stored type vars are overridable and observable. When they are assigned new values, all their property observers defined in the class hierarchy are called, calling order is not significant, the reason of which will be addressed later.
>> 
>> Unfortunately, there is one last catch: if more than one classes assign a value to the property within their didSet observer, the final result is undefined.
>> 
>> This issue can not even be resolved by ordering observer calls from top to bottom in the class hierarchy because we can not meaningfully define the order within the same level if there are more than one subclasses in it.
>> 
>> So one addition to option 4:
>> 4-1. Stored type vars’ didSet observers are not allowed to reassign value to the observed property.
>> 
>> But it is such a corner case that probably this limitation is acceptable.
>> 
>> What do you think? Is my understanding of class objects correct? Which option do you like best?
>> 
>> - Ling
>> 
>>> On Dec 8, 2015, at 5:40 PM, Jordan Rose <jordan_rose at apple.com <mailto:jordan_rose at apple.com>> wrote:
>>> 
>>> 
>>>> On Dec 7, 2015, at 19:12, Ling Wang via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>> 
>>>> Besides, we shouldn’t use `final` to denote storage at all for `final` is about inheritability/overridability not about storage.
>>>> 
>>> 
>>> Please remember that stored properties can be overridden in Swift:
>>> 
>>> class Base {
>>>   /*instance*/ var flag: Bool = true
>>> }
>>> 
>>> class Sub : Base {
>>>   override var flag: Bool {
>>>     didSet {
>>>       if flag { print("The flag was set!") }
>>>     }
>>>   }
>>> }
>>> 
>>> This is theoretically possible at the class level as well, but doesn't really make sense for what's currently spelled "static var", since you'd be accessing the same storage in different ways. Does the proposal mean that a stored "type var" is non-overrideable, but a computed "type var" is overrideable, implicitly?
>>> 
>>> Jordan
>> 
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151210/2d0a3f3d/attachment.html>


More information about the swift-evolution mailing list