<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=""><div class="">Okay, resurrecting this proposal (which some may not have seen due to it having been sent over the holiday break), because I thought of a really major use case.</div><div class=""><br class=""></div><div class="">In addition to the obvious use cases, like init methods that get too long because they can’t be broken up into smaller methods, duplicated code between init() and init(coder:), and the like, I just realized that this feature, if implemented with an appropriate annotation to make it bridgeable from Objective-C headers, could make NSDocument not suck in Swift.</div><div class=""><br class=""></div><div class="">Currently we have this situation:</div><div class=""><br class=""></div><div class="">class MyDoc: NSDocument {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>var foo: Foo!</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>var bar: Bar!</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>override func readFromData(data: NSData, ofType: String) throws {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>self.foo = // parse something from the data</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>self.bar = // parse something from the data</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>// etc.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class="">}</div><div class=""><br class=""></div><div class="">There are a number of problems with this.</div><div class=""><br class=""></div><div class="">1. Since there’s no way to know what any of the document’s state is supposed to be at the time the class is initialized, pretty much everything has to be optional. And in practice, these are going to be of the implicitly-unwrapped variety much of the time, since many of these variables have no real reason to be optional once readFromData() has been called, and no one is going to want to needlessly unwrap these things Every. Damn. Time.</div><div class=""><br class=""></div><div class="">2. Unlike code in init methods, the code above will call the setters for foo and bar, *not* just set the underlying ivars directly. This means that willSets and didSets, as well as things like KVO notifications, will get fired. If the document has a decent number of properties, it is easy for properties that haven’t been initialized yet to be inadvertently called as a result of the side-effects of setting properties in readFromData(), leading to a crash.</div><div class=""><br class=""></div><div class="">3. Redesigning NSDocument to work better with Swift is an option, except that as the language currently stands, this would be hard to do and make it work similarly to how it currently does. Particularly, the current system provides several override points. If you just need the file contents, you override readFromData(). However, if you need to access file metadata or something beyond just the contents, you can override readFromURL() instead. The default implementation of readFromURL(), of course, calls readFromData(), so you can choose which override point is more appropriate and use that. The obvious solution to this would be to turn the readFrom*() methods into initializers, but for init(URL:) to call init(data:) in its default implementation, it would need to be declared as a convenience initializer—which can’t be overridden.</div><div class=""><br class=""></div><div class="">If we had a way to declare methods as init helpers, a way to annotate these in Objective-C, and an annotation to put on the designated initializer indicating which init helpers the initializer will call, this might be able to be all separated out. I’m not sure whether NSDocument could be made to conform to this as is, but a hypothetical redesigned document class could look something like this:</div><div class=""><br class=""></div><div class="">class NSHypotheticalDocument: NSObject {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>@uses_helper(readFromURL:ofType:) init(URL url: NSURL, ofType type: String) throws {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>try self.readFromURL(url, ofType: type)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>super.init()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>@uses_helper(readFromData:ofType:) init_helper readFromURL(url: NSURL, ofType type: String) throws {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>let data = try NSData(contentsOfURL: url, options: [])</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>try self.readFromData(data, ofType: type)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>init_helper readFromData(data: NSData, ofType type: String) throws {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>throw NSCocoaError.FeatureUnsupportedError</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// other stuff</div><div class="">}</div><div class=""><br class=""></div><div class="">A subclass could now override one of the two helper methods, and since the designated initializer is documented as calling these particular helpers, the compiler could know that any properties set in an overridden helper method would be set by the end of the super.init call in the subclass’s own initializer, and thus these properties would be set without side effects. This could allow properties in document classes to be safely set without unnecessary use of optionals.</div><div class=""><br class=""></div><div class="">I dunno, what do you think?</div><div class=""><br class=""></div><div class="">Charles</div><br class=""><div><blockquote type="cite" class=""><div class="">On Dec 15, 2015, at 5:59 PM, Ross O'Brien via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class=""><div style="font-size:13px" class="">Hi all,</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">I'm a new member of the list, so apologies if this is a duplicate of an existing idea or if there's already a way to do this in Swift 2.1 that I've missed.</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">In Objective C, and C-like languages, an initialiser function represents a stage after allocation of memory where properties are given values. In Swift, init appears to precede (or overlap with) allocation. The benefit of this is that for type-safety reasons, all properties of a type (or new properties of a derived type) can be verified as having values. The disadvantage, and one of the stumbling blocks for those who learned Objective-C, is that until all the properties have values, the instance does not exist and instance functions cannot be called.</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">There's an invisible threshold in Swift init() functions marking this transition. In derived classes it's the point where super.init() is called - after the derived type has provided initial values, but before any type functions can be called.</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">Some types have multiple initialisers, and may be duplicating a lot of code in those distinct inits before they cross the threshold. This code can't be refactored into an instance function because the instance doesn't exist yet. The instance function may not even require the use of any properties of the type.</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">If the compiler can read an init function and its varied control flow and determine a threshold where all properties have values, presumably it can read the code of any function called before that threshold, determine which properties they read and which they assign to, and provide a warning if a path assigns to a constant a second time, etc.. But this isn't currently happening.</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">I'm guessing there are multiple contributing factors for this: the combinatorial explosion of possible control flow paths with functions (particularly if they're recursive); the possibility that the function calls are used by the compiler to mark the end of a control flow path, by which point it can determine whether everything has a value; the function genuinely can't exist without allocation. I don't know the reasons but I'd be interested to learn them.</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">I'm proposing the keyword 'selfless' for a function which could be called before the threshold. It either only uses local properties or properties belonging to the type - never to the 'super' type (in the case of a derived class). It can't call any instance functions which aren't themselves selfless.</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">Example of use:</div><div style="font-size:13px" class="">class FooView : UIView</div><div style="font-size:13px" class="">{</div><div style="font-size:13px" class=""> var property : Int</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class=""> init()</div><div style="font-size:13px" class=""> {</div><div style="font-size:13px" class=""> initialiseProperty()</div><div style="font-size:13px" class=""> super.init()</div><div style="font-size:13px" class=""> }</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class=""> init(frame:CGRect)</div><div style="font-size:13px" class=""> {</div><div style="font-size:13px" class=""> initialiseProperty()</div><div style="font-size:13px" class=""> super.init(frame)</div><div style="font-size:13px" class=""> }</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class=""> selfless func initialiseProperty()</div><div style="font-size:13px" class=""> {</div><div style="font-size:13px" class=""> property = 4</div><div style="font-size:13px" class=""> }</div><div style="font-size:13px" class="">}</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">Is this something of interest?</div><div style="font-size:13px" class=""><br class=""></div><div style="font-size:13px" class="">Regards,</div><div style="font-size:13px" class="">Ross O'Brien</div></div>
<img src="https://u2002410.ct.sendgrid.net/wf/open?upn=kND2tqgLiolwf1-2Bhgg7fFiaPS455NT9j3CATwJCX709M8hiwx2WKZSJCELkcBqGY-2Fm7C5QG8i1r7K0obNubBiUnShJis2gooF4fSIiXTBBMSr9UgX2ZROjIqp9RIgB89m43DWaP-2FcgQ-2FrZWu-2B6mJF5gC0gzYISEBCFYK-2FshlecsFTtDb0vQs-2FEc2sKG0am9nKzpi5IXOiEt-2B7ZJ2bHjK6frTL1VmI598aZojB9-2FCUBk-3D" alt="" width="1" height="1" border="0" style="height:1px !important;width:1px !important;border-width:0 !important;margin-top:0 !important;margin-bottom:0 !important;margin-right:0 !important;margin-left:0 !important;padding-top:0 !important;padding-bottom:0 !important;padding-right:0 !important;padding-left:0 !important;" class="">
_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>