On Dec 21, 2015, at 11:32 AM, Matthew Johnson via swift-evolution 
<swift-evolution@swift.org> wrote:
> I have completed a draft of the proposal I have been working on for flexible 
> memberwise initialization.  I am really looking forward to your input and 
> will be refining the proposal based on our discussion.
> 
> I am including a current snapshot of the proposal in this message.  I will 
> keep the proposal up to date on Github at this link:
> 
> https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md
>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md>
This is a really really interesting approach, I really like it.  Detailed 
comments below, I’m skipping all the stuff I agree with or have no additional 
comments on:

> 
> Flexible Memberwise Initialization
> 
>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#replacing-the-current-memberwise-initializer>Replacing
>  the current memberwise initializer
> 
> struct S {
>     let s: String
>     let i: Int
> 
>     // user declares:
>     memberwise init() {}
It never occurred to me to allow a body on a memberwise initializer, but you’re 
right, this is a great feature.  I love how this makes memberwise init behavior 
a modifier on existing initializers.
> Properties with initial values
> 
> struct S {
>     let s: String = "hello"
>     let i: Int = 42
> 
>     // user declares:
>     memberwise init() {}
>     // compiler synthesizes:
>     init(s: String = "hello", i: Int = 42) {
>         self.s = s
>         self.i = i
>     }
> }
In the case of let properties, I’m uncomfortable with this behavior and it 
contradicts our current init rules (the synthesized code isn’t legal).  Please 
change the example to var properties, and then it’s can fit with the model :-). 
 

That said, I think the interaction of explicit initializers and memberwise 
initializers begs discussion.  It would be a much simpler model to only get 
memberwise parameters for properties without an explicit init.  Have you 
considered this model, what are the tradeoffs with allowing vars to overwrite 
them?  Allowing an explicit init to be overwritten by the memberwise 
initializer seems potentially really confusing, and since you allow explicit 
arguments on inits, this could always be handled manually if someone really 
really wanted it.  For example, they could write:

memberwise init(s : String) {
  self.s = s
}

If they wanted to get the sugar of memberwise inits (presumably for other 
properties without an explicit init) but still allow one to be overwritten.


>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#partial-memberwise-initialization>Partial
>  memberwise initialization
> 
> struct S {
>     let s: String
>     let i: Int
> 
>     // user declares:
>     memberwise init() {
>         i = getTheValueForI()
>     }
>     // compiler synthesizes (suppressing memberwise initialization for 
> properties assigned in the initializer body):
>     init(s: String) {
>         self.s = s
>         // body of the user's initializer remains
>         i = getTheValueForI()
>     }
> }
This doesn’t seem like the right behavior to me.  The compiler shouldn’t be in 
the business of scanning the body of the init to decide what members are 
explicitly initialized.  I’d suggest that the model simply be that the contents 
of the {} on a memberwise init get injected right after the memberwise 
initializations that are done.  This mirrors how properties with default values 
work.


>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#lazy-properties-and-incompatible-behaviors>lazy
>  properties and incompatible behaviors
> 
> struct S {
>     let s: String
>     lazy var i: Int = InitialValueForI()
> 
>     // user declares:
>     memberwise init() {
>     }
>     // compiler synthesizes:
>     init(s: String) {
>         self.s = s
>         // compiler does not synthesize initialization for i 
>         // because it contains a behavior that is incompatible with 
>         // memberwise initialization
>     }
> }
Yes, this is likely to be subsumed into JoeG’s "behaviors” proposal.  In the 
meantime, I’d suggest no behavior change for lazy properties.


>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#nomemberwise-properties>@nomemberwise
>  properties
> 
> struct S {
>     let s: String
>     @nomemberwise let i: Int
> 
>     // user declares:
>     memberwise init(configuration: SomeTypeWithAnIntMember) {
>         i = configuration.intMember
>     }
>     // compiler synthesizes:
>     init(configuration: SomeTypeWithAnIntMember, s: String) {
>         self.s = s
>         i = configuration.intMember
>     }
> }
@nomemberwise is an interesting extension, but since it is a pure extension 
over the basic model, I’d suggest moving this into a “possible future 
extensions” section.  The proposal doesn’t need this feature to stand on its 
own. 

>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#delegating-and-convenience-initializers>delegating
>  and convenience initializers
> 
> struct S {
>     let s: String = "hello"
>     let i: Int = 42
> 
>     // user declares:
>     memberwise init() {}
>     // compiler synthesizes:
>     init(s: String = "hello", i: Int = 42) {
>         self.s = s
>         self.i = i
>     }
> 
>     // user declares:
>     memberwise init(describable: CustomStringConvertible) {
>         self.init(s: describable.description)
>     }
>     // compiler synthesizes (adding forwarded memberwise parameters):
>     init(describable: CustomStringConvertible, i: Int = 42) {
>         self.init(s: describable.description, i: i)
>     }
> }
This example is introducing two things: convenience inits, but also parameter 
arguments.  For the sake of the proposal, I’d suggest splitting the parameter 
arguments out to its own discussion.  It isn’t clear to me whether the 
memberwise initializers should come before explicit arguments or after, and it 
isn’t clear if we should require the developer to put something in the code to 
indicate that they exist.  For example, I could imagine a syntax like this:

memberwise init(…) {}  
memberwise init(describable: CustomStringConvertible, ...) {

Where the … serves as a reminder that the init takes a bunch of synthesized 
arguments as well.

>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#subclass-initializers>subclass
>  initializers
> 
> class Base {
>     let baseProperty: String
> 
>     // user declares:
>     memberwise init() {}
>     // compiler synthesizes:
>     init(baseProperty: String) {
>         self.baseProperty = baseProperty
>     }
> }
> 
> class Derived: Base {
>     let derivedProperty: Int
> 
>     // user declares:
>     memberwise init() {}
>     // compiler synthesizes (adding forwarded memberwise parameters):
>     init(baseProperty: String, derivedProperty: Int) {
>         self.derivedProperry = derivedProperty
>         super.init(baseProperty: baseProperty)
>     }
> }
This also seems unclear to me.  We’re generally very concerned about tightly 
coupling derived classes to their bases (in an API evolution scenario, the two 
classes may be in different modules owned by different clients).  Further, the 
base class may have multiple inits, and it wouldn’t be clear which one to get 
the arguments from.


>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#detailed-design>Detailed
>  design
> 
>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#syntax-changes>Syntax
>  changes
> 
> This proposal introduces two new syntactic elements: the memberwise 
> initializer declaration modifier and the @nomemberwise property attribute.
> 
As before, I’d suggest splitting @nomemberwise out to a “potential future 
extensions section”.

> Algorithm
> 
> The steps described in this section will be followed by the compiler when it 
> performs memberwise initialization synthesis. These steps supercede the 
> synthesis of initialization for properties with initial values that exists 
> today.
> 
> When the compiler performs memberwise initialization synthesis it will 
> determine the set of properties that are eligible for synthesis that are not 
> directly initialized in the body of the initializer. It will then synthesize 
> parameters for them as well the initialization of them at the beginning of 
> the initializer body.
> 
I’d strongly suggest considering a model where properties that have an explicit 
initializer don’t get a memberwise init.

Have you considered whether computed properties make sense to loop into your 
model?

Typo "initialzier”.

> 
>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#objective-c-class-import>Objective-C
>  Class Import
> 
> Objective-C frameworks are extremely important to (most) Swift developers. In 
> order to provide the call-site advantages of flexible memberwise 
> initialization to Swift code using Cocoa frameworks this proposal recommends 
> introducing a MEMBERWISE attribute that can be applied to Objective-C 
> properties and initializers.
> 
This is also an orthogonal extension on top of the base proposal.  I’d suggest 
splitting it off to a “possible future extensions” section as well.

> 
>  
> <https://github.com/anandabits/swift-evolution/blob/flexible-memberwise-initialization/proposals/NNNN-flexible-memberwise-initialization.md#impact-on-existing-code>Impact
>  on existing code
> 
> The changes described in this proposal are strictly additive and will have no 
> impact on existing code.
> 
> One possible breaking change which may be desirable to include alongside this 
> proposed solution is to elimintate the existing memberwise initializer for 
> structs and require developers to specifically opt-in to its synthesis by 
> writing memberwise init() {}. A mechanical transformation is possible to 
> generate this declaration automatically if the existing memberwise 
> initializer is removed.
> 
I think that that would be very interesting to discuss, but I lean towards 
keeping our existing model for synthesizing a memberwise init if there is no 
other init in a struct (and we should do it for classes as well).  Requiring 
someone to write "memberwise init() {}” is just boilerplate, and producing it 
as “internal” avoids most of the problems from being something undesirable 
being generated.  That said, I see the argument that being more explicit is 
good.

Overall, I’m a huge fan of this proposal and the direction you’re going in.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to