<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Dec 22, 2015, at 8:46 AM, Matthew Johnson <<a href="mailto:matthew@anandabits.com" class="">matthew@anandabits.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Hi Chris,</div><div class=""><br class=""></div><div class="">I have given your feedback a lot of thought and have taken another run at this proposal from a slightly different angle. I believe it is a significant improvement. </div></div></div></blockquote><br class=""></div><div>Hi Matthew,</div><div><br class=""></div><div>I continue to really like the approach and direction. Here’s an attempt to respond to both of your responses, I hope this comes across somewhat coherent:</div><div><br class=""></div><div><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class=""> I hope you’re willing to entertain on some discussion on some aspects of the proposal that you are not immediately sold on. :)</div></div></div></blockquote><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class=""><br class=""></div><div class="">Yes, absolutely.</div><div class=""><br class=""></div></div></div></div></div><div><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class="">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 :-). </div></div></div></div></blockquote><div class=""><br class=""></div><div class="">I understand that this is not legal under the current init rules because the compiler currently produces a synthesized initialization of the properties to their initial values at the beginning of the initializer. I actually don’t like this behavior because it doesn’t allow an initializer body to initialize a let property with an "initial value”. </div><div class=""><br class=""></div><div class="">I’m pretty sure I’m not alone in this. People want to use immutable properties with “default values” (I believe this is what most Swift developers are calling what the Swift team refers to as “initial values”) without a requirement that all instances actually have that value. It was actually pretty surprising to me, and I’m sure to others as well, to discover this limitation. I actually thought it was a limitation of the current implementation rather than something that was intentionally designed. I’m surprised to hear otherwise.</div></div></div></blockquote></div><br class=""><div class="">I completely agree with your desire to support this, and I’m sure that if we did, that a ton of people would use it and love it. However, I really don’t think this is a good idea.</div><div class=""><br class=""></div><div class="">There are two major problems:</div><div class=""><br class=""></div><div class="">Problem 1: supporting this would *prevent* us from allowing memberwise initializers to be public. A really important feature of the memberwise design is that it is “just sugar” and that people can write the initializer out long hand if needed (e.g. if they want to add or reorder members without breaking API). With your proposed design, I could write:</div><div class=""><br class=""></div><div class="">public class X {</div><div class=""> let a = 42</div><div class=""> public memberwise init(...) {}</div><div class="">}</div><div class=""><br class=""></div><div class="">and use it with: X(a: 17). However, there is no way in swift to write that initializer out longhand. This is a critical problem to me.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">Problem 2: This can cause very surprising performance issues, because it forces the let property to be stored. With the previous example, it is a goal for us to be able to compile:</div><div class=""><br class=""></div><div class="">public class X {</div><div class=""> let a = 42</div><div class="">}</div><div class=""><br class=""></div><div class="">into the equivalent of:</div><div class=""><br class=""></div><div class=""><div class="">public class X {</div><div class=""> var a : Int { return 42 }</div><div class="">}</div></div><div class=""><br class=""></div><div class="">because people like to use local lets as manifest constants (avoiding “magic numbers”). With your proposal, we’d lose this capability, and we’d have to store them any time there is a memberwise initializer.</div><div class=""><br class=""></div><div class="">Neither of these problems apply to vars, which is why I think we can support vars in this model, but not lets.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class="">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:</div><div class=""><br class=""></div><div class="">memberwise init(s : String) {</div><div class=""> self.s = s</div><div class="">}</div><div class=""><br class=""></div><div class="">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.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Personally, I think there is a lot of value in allowing memberwise initialization for properties that do contain an initial value. Imagine a hypothetical Swift version of Cocoa Touch. UILabel might have initial values for text, font, textColor, etc but still want to allow clients to provide memberwise initialization arguments to override the default value. I think there are many cases like this both in UI code and elsewhere. </div></div></div></blockquote></div><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class=""><br class=""></div><div class="">I think I confused the issue. If we have to support properties that have a default value, then the model I’m advocating for is that this:</div><div class=""><br class=""></div><div class="">class C {</div><div class=""> let x : Int</div><div class=""> var y : Int = foo()</div><div class=""><br class=""></div><div class=""> memberwise init(...) {}</div><div class="">}</div><div class=""><br class=""></div><div class="">compile into:</div><div class=""><br class=""></div><div class="">init(x : Int, y : Int = foo()) {</div><div class=""> self.x = x</div><div class=""> self.y = y</div><div class="">}</div><div class=""><br class=""></div><div class="">Pertinent points of this are that lets without a default value would still turn into arguments, and that any side effects of the var initializer would be squished. Another potential model is to compile it to:</div><div class=""><br class=""></div><div class=""><div class="">init(x : Int, y : Int) {</div><div class=""> self.x = x</div><div class=""> self.y = foo()</div><div class="">}</div><div class=""><br class=""></div><div class="">which is guaranteed to run the side effect, but requires y to be specified. I do not think it is a good model to compile it to:</div><div class=""><br class=""></div><div class=""><div class="">init(x : Int, y : Int? = nil) {</div><div class=""> self.x = x</div><div class=""> self.y = y ?? foo()</div><div class="">}</div></div><div class=""><br class=""></div><div class="">because that would allow passing in an Int? as the argument. The final model (which I know you don’t like) is for memberwise initializers to *only* apply to properties without a default value.</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class="">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.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">The model does inject the synthesized memberwise initialization just prior to the body of the initializer. </div><div class=""><br class=""></div><div class="">As written the proposal does scan the body of the init in order to determine which properties receive memberwise initialization. The idea here is that it provides additional flexibility as it allows specific initializers to “opt-out” of memberwise initialization synthesis for some properties while receiving it for others.</div></div></div></blockquote></div><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class=""><br class=""></div></div></div></div><div class="">This is a very problematic model for me, because it can lead to serious surprises in behavior, and in the case of lets, shares the problems above with not allowing one to define the long-hand form explicitly.</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class="">I don’t think the proposal changes lazy properties. </div></div></blockquote><br class=""></div><div class="">I agree, I was saying that I like that :)</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class="">@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. </div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Allowing type authors to restrict memberwise initialization to a subset of properties is an important aspect of “flexible” memberwise initialization IMO. In my mind, this is about allowing the author of a type to segment properties that are “user configurable” from properties that are implementation details. </div></div></div></blockquote><br class=""></div><div class="">I understand, but it is a pure extension to the basic proposal. The proposal is complex enough as it is, so inessential parts should be split out for discussion and implementation. I’m not saying that we shouldn’t do @nomemberwise in (e.g.) the swift 3 timeframe, I’m saying that it should be a separate discussion informed by the design and implementation process of the base proposal.</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class="">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.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">I know there are a lot of complexities here. It is certainly possible I am missing some showstoppers, especially related to resilience, etc.</div><div class=""><br class=""></div><div class="">User code would of course need to provide sufficient parameters to disambiguate the call to the base class initializer.</div><div class=""><br class=""></div><div class="">The goal in this section is to enable memberwise initialization to be used in a class hierarchy. I think UIKit is a good case to consider for the sake of discussion. In a hypothetical Swift version of UIKit we would want to allow memberwise initialization of appearance attributes regardless of where they are declared in the class hierarchy. (I would hope a designed-for-Swift UIKit would not rely so heavily on inheritance, but nevertheless I think it makes good case study for discussing flexible memberwise initialization).</div><div class=""><br class=""></div><div class="">The same mechanism that handles inheritance should also be able to handle delegating and convenience initializers. The idea is to write a delegating or convenience initializer that wraps the non-memberwise portion of a memberwise initializer while still allowing the memberwise initialization to “flow through” and be visible to callers of the delagating / convenience initializer.</div><div class=""><br class=""></div><div class="">I attempted to outline a basic strategy for handling propagation of memeberwise initialization through the inheritance hierarchy as well as other cases of initializer delegation in the detailed design. It is definitely not complete, almost certainly has flaws and omissions, etc. I’m hoping we can flesh out the details through community discussion.</div><div class=""><br class=""></div><div class="">I hope you will agree that it is important to support inheritable memberwise initialization and that we do need to address this in some way.</div></div></div></blockquote></div><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class=""><br class=""></div></div></div></div><div class="">I don’t agree. memberwise is a sugar feature intended to handle the most common scenarios. One of the major reasons we don’t support memberwise inits in classes today is that we have no ability to know what superclass init to call (and root classes aren’t interesting enough to provide a solution that only works for them). </div><div class=""><br class=""></div><div class="">Given that your proposal allows user code to be added to the synthesized init, I’d really strongly prefer that this be a compile time error, because super.init was never invoked (ok ok, if the superclass has an "init()” as its only DI then yes, we can synthesize it by default like we do today).</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class="highlight highlight-source-swift" style="box-sizing: border-box; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; line-height: 25px; background-color: rgb(255, 255, 255);"><pre class="" style="box-sizing: border-box; overflow: auto; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; margin-top: 0px; margin-bottom: 0px; line-height: 1.45; padding: 16px; background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-wrap: normal; word-break: normal;"><span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">class</span> Derived: Base {
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">let</span> derivedProperty: <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">Int</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// user declares:</span>
memberwise <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">init</span>() {}
<span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// compiler synthesizes (adding forwarded memberwise parameters):</span>
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">init</span>(baseProperty: <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">String</span>, derivedProperty: <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">Int</span>) {
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">self</span><span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">.</span>derivedProperry <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">=</span> derivedProperty
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">super</span><span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">.</span><span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">init</span>(baseProperty: baseProperty)
}
}</pre></div></div></div></blockquote></div></div></div></blockquote></div></div></blockquote><div class=""><br class=""></div></div></div></div></div></div><div class="">Instead, the user should have to write:</div><div class=""><br class=""></div><div class="">memberwise init(baseProperty : Int, ...) {</div><div class=""> super.init(baseProperty: baseProperty)</div><div class="">}</div><div class=""><br class=""></div><div class="">This doesn’t reduce the utility of this feature, and it preserves separability of class from superclass.</div><div class=""><br class=""></div><div class="">Thank you again for pushing this forward!</div><div class=""><br class=""></div><div class="">-Chris</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div></body></html>