[swift-evolution] Fwd: [Proposal Draft] Flexible memberwise initialization

Matthew Johnson matthew at anandabits.com
Thu Jan 7 17:23:34 CST 2016



> Begin forwarded message:
> 
> From: Jordan Rose <jordan_rose at apple.com>
> Subject: Re: [swift-evolution] [Proposal Draft] Flexible memberwise initialization
> Date: January 6, 2016 at 9:29:19 PM CST
> To: Matthew Johnson <matthew at anandabits.com>
> Cc: Joe Groff <jgroff at apple.com>, swift-evolution <swift-evolution at swift.org>
> 
>> 
>> On Jan 6, 2016, at 19:07 , Matthew Johnson via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> 
>> 
>> Sent from my iPad
>> 
>> On Jan 6, 2016, at 8:43 PM, Joe Groff <jgroff at apple.com <mailto:jgroff at apple.com>> wrote:
>> 
>>> 
>>>> On Jan 6, 2016, at 6:31 PM, Matthew Johnson <matthew at anandabits.com <mailto:matthew at anandabits.com>> wrote:
>>>> 
>>>> 
>>>> 
>>>> Sent from my iPad
>>>> 
>>>>> On Jan 6, 2016, at 7:57 PM, Joe Groff <jgroff at apple.com <mailto:jgroff at apple.com>> wrote:
>>>>> 
>>>>> 
>>>>>> On Jan 6, 2016, at 3:39 PM, Matthew Johnson <matthew at anandabits.com <mailto:matthew at anandabits.com>> wrote:
>>>>>> 
>>>>>> 
>>>>>>> On Jan 6, 2016, at 5:08 PM, Joe Groff <jgroff at apple.com <mailto:jgroff at apple.com>> wrote:
>>>>>>> 
>>>>>>> I find it surprising that you key the initialization of 'var's based on whether their setter is visible or not. Initialization is not the same as setting, and memberwise initializers within the definition have private access to the member anyway; this is why `let` properties can be initialized, after all. `private(set)` is also a way for an API to communicate that a property is read-only without promising that it is or will always be immutable, so I think it's important that a 'private(set) var' be as capable as a 'let' to the maximum degree possible.
>>>>>>> 
>>>>>>> -Joe
>>>>>> 
>>>>>> Hi Joe,
>>>>>> 
>>>>>> Thanks for bringing this topic up and moving the discussion to the list (it’s hard to get into details on Twitter).
>>>>>> 
>>>>>> I am definitely sympathetic to the points you raise.  The problem is that there is no good solution to this without involving at least one of the future enhancements.  Chris feels strongly that we need to focus on the core functionality for the initial proposal so we must choose a solution without them.
>>>>>> 
>>>>>> Using the `var` setter visibility is the least bad option in my mind.  There are many times when a var might represent internal state that a user is allowed to read, but should never be allowed to specify, whether via initialization or otherwise.  These will have `private(set)` visibility.  
>>>>>> 
>>>>>> If we allow memberwise initialization to expose them it will be a useless feature for types that contain a var like this.  There will not be any way to specify that they should not participate in memberwise initialization without at least one of the future enhancements.  On the other hand, if you do wish to expose them via the initializer it is easy to add a parameter and initialize the `var` manually.
>>>>>> 
>>>>>> This is also a safer solution.  The author of the type has specifically stated that users should not be setting the value of the `var`.  Maybe that doesn’t apply to initialization, but it is not the right decision to assume that IMO, at least without the ability to specify `init` visibility independently if desired.
>>>>>> 
>>>>>> In the case of `let`, if we do not use the only access control modifier they are allowed to have they would not be able to participate in memberwise initialization at all.
>>>>>> 
>>>>>> I think the proposal makes the least bad choice we can make without expanding it to include the ability to specify init visibility (which I would support if the core team was willing to do that).
>>>>> 
>>>>> 
>>>>> I'm not sure what you mean by init visibility.
>>>> 
>>>> The proposal suggests a possible future enhancement for specifying access control for init distinct from get and set like: 'private public(init)'.  This would be available to both 'let' and 'var' properties and would allow a single memberwise initialization rule to be used for all properties.
>>>> 
>>>>> I feel like all these arguments could also be made for 'let'; what makes a get-only var different from the initializer's perspective?
>>>> 
>>>> This is true in some sense.  The difference is that 'let' properties only have access control for a getter making it the only option we have if they are allowed to participate in memberwise initialization.  
>>> 
>>> That line of reasoning doesn't make sense. 'let' properties only have access control for a getter because they never have a setter; the setter is effectively always private.
>> 
>> Maybe I'm not communicating very clearly, of course that is the case.  But 'let' properties should still be able to participate in memberwise initialization.  If we are going to apply access control rules (which I think is important if we allow the automatic model for property eligibility) we have to use the only access control setting available to them in the language as it exists.  
>> 
>> Allowing a distinct access control to be specified for initialization would solve this problem.  Adopting the opt-in model for property eligibility avoids the access control complications altogether.  I like that model quite a bit but Chris wanted to stick with the automatic model because it doesn't require a new declaration modifier on properties.
>> 
>>> 
>>>> Here's an example.  Think of a progress property.  That will be a far with a public getter but a private setter.  It would clearly be wrong if that were exposed by an initializer.  If we use the getter for a 'var' in memberwise initialization it would be exposed to all memberwise initializers under the current proposal (without any of the enhancements).  
>>> 
>>> What type would have a progress property *and* useful memberwise initialization of its other state? In fact, what type has private state at all and useful memberwise initialization of its public state? I'm having trouble seeing these as realistic use cases, since as soon as you have some form of state encapsulation you usually also want to curate the initialization of that state. At that point, hiding the memberwise code generation starts obscuring the logic rather than helping IMO, especially since there's probably validation that should be interleaved with it.
>> 
>> I would have to think about the progress example longer than I have time at the moment to get specific.
>> 
>> UI widgets very commonly have private state and also many appearance attributes that are user configurable and great candidates for memberwise initialization.  Many of them are likely to have 'public private(set) var' properties as well which should not be exposed to callers during initialization.
>> 
>>> 
>>> The rules you've proposed for what properties correspond to memberwise parameters are already tricky. It would be a nice simplification to say that all stored properties that need initialization end up in the memberwise initializer, regardless of visibility.
>> 
>> That would severely cripple the feature IMO.  It would make it "all or nothing" and be useless in cases such as UI widgets where you need both private state and public state. 
>> 
>> It's common to just have callers set those attributes to desired values immediately after I initialization completes.  Many people write small helper libraries to make this less verbose, some of which have been discussed on this list.  This is a failure of the initialization capabilities of the language IMO.  This proposal is an attempt to make it easier to write types that can be fully configured during initialization.
>> 
>> The opt-in model avoids the complexity while retaining flexibility at the cost of requiring a declaration modifier in properties.  I think this is a very reasonable tradeoff to make and would be happy to support that approach as an alternative to or in addition to the "automatic" approach used in the current proposal.
> 
> I shared Joe's concern at first, but then I thought about the three kinds of public properties:
> 
> - freely settable by the user (var)
> - read-only outside of the module (internal(set) var)
> - constant for the life of the object (let)
> 
> Obviously there's no harm in putting the first into the memberwise initializer. The last could go either way—maybe it's a unique ID generated by the system, maybe it's configuration set by the user—but I would guess the latter is more common than the former.
> 
> So what kind of things are read-only properties used for? A lot of computed properties are read-only: things derived from other aspects of the object's state, sometimes projections of internal state that you don't want to expose in its entirety (or its raw representation). But other times they represent output, such as the 'terminationStatus' of an NSTask, or the 'URLs' of an NSOpenPanel. There just aren't that many things where you can set the initial value of something, but can't change it later…but it can still change out from under you.
> 

Thanks Jordan.  This is what I mean when I say the rules specified in the proposal are likely to do the right thing most of the time.  I’m glad you brought up some specific examples.

When you think about the how different rules will behave in practice and how that compares to what you want, I think using the setter visibility for a `var` will be what you want more often.  

Aside from that, the feature is still useful when this doesn’t do what you want when using the `var` setter visibility and it is probably not useful when it doesn’t do what you want if we use the `var` getter.  If it exposes a property that needs to be private and you don’t have a way to prevent that you won’t be able to use the feature.

> Therefore, even though it adds complexity to the proposal, I think Matthew's come up with the right tradeoff here.
> 
> Jordan
> 
> P.S. I have some concerns/feedback of my own, but I'll put that in a separate message.

Looking forward to hearing more from you.

Matthew

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160107/05c26a04/attachment.html>


More information about the swift-evolution mailing list