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

Ling Wang an00na at gmail.com
Thu Dec 10 19:56:53 CST 2015


But can your code compile and run? How can you access instance properties via class interface?
I didn't bring up this behavior of computed class variables as an argument for unifying "class" and "static". I brought up it to demonstrate the similarity between computed and stored type properties, even on limitation/issue level, which is an argument for unifying "class" and "static". 

- Ling
    _____________________________
From: Jordan Rose <jordan_rose at apple.com>
Sent: Thursday, December 10, 2015 7:06 PM
Subject: Re: [swift-evolution] Unify `static` and `class` keywords
To: Ling Wang <an00na at gmail.com>
Cc: swift-evolution <swift-evolution at swift.org>


          Here is your code, minus the "class" qualifier.       
                var backingVarForFooI = 0                   
                   class Foo {                       var i: Int {      // was "class"                           get {                               return backingVarForFooI                           }                                                      set {                               backingVarForFooI = newValue                               print("Foo.i.set \(newValue)")                           }                       }                   }                   
                   class Bar: Foo {                       override var i: Int {      // was "class"                           didSet {                               print("Bar.i.didSet \(i)")                           }                       }                   }                   
                   print(Bar.i)                   Foo.i = 3                   print(Bar.i)                
       Now, clearly this is rather silly code, but my point is "if you choose to use a global variable as the backing storage of a computed property, you can mutate it in multiple ways". Whether the computed property is a class property or an instance property doesn't affect that.       
       That's not an argument against unifying "class" and "static". It's just pointing out that the existing behavior of computed class variables isn't an argument    for unifying "class" and "static" either.       
       Jordan       
    
              On Dec 10, 2015, at 12:19, Ling Wang <     an00na at gmail.com> wrote:        
                           I just realized that I wrote `overload` when I meant `override` in some places. Sorry for that.                   
      I didn’t say it is a bug, just an issue. An issue like I only get notified when I draw money from the bank account I inherited from my father but not when my father draw from it. :p             
                   The underlying implementation may match match that for instance computed properties. But we can't demonstrate this behavior on instance properties, can we? How can a Swift user trigger super setter without triggering subclass observer when writing to an instance property?                   
                   There is no clear solution to overriding setter and overriding with observer at the same time.                   
                   When I talked about the issue, my major point is that there is no necessary and meaningful difference between stored and calculated type properties, even the issues are the same, so we can unite them with a single keyword.                   
I thought I got it from your first mail, but after these discussion I got lost:( What exactly is your concern of stored type properties from my proposal? Why doesn't it apply to calculated type properties?       
       
               - Ling        
                 
                                          On Dec 10, 2015, at 12:17 PM, Jordan Rose <            jordan_rose at apple.com> wrote:                      
                                                              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> 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> wrote:                                              
                                                                                                
                                                                                                          On Dec 7, 2015, at 19:12, Ling Wang via swift-evolution <                            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/20151211/eb9ef4bf/attachment.html>


More information about the swift-evolution mailing list