Ultimately, while these are good points, I feel the full mechanism for class clusters belong in a separate proposal. The focus for this one I believe should be the underlying mechanism of factory initializers; should that be approved, then we can focus on adding additional features on top of it.
That being said, I’ve written up essentially a final version of the proposal, which you can find here: https://github.com/rileytestut/swift-evolution/blob/master/proposals/NNNN-factory-initializers.md. Assuming everyone is happy with it, I’ll send a pull request in the next few days to the main-repo. But please, give any last minute feedback now! > On Mar 24, 2016, at 5:12 PM, Jonathan Hull <[email protected]> wrote: > > Comments inline. > >> On Mar 24, 2016, at 12:10 AM, Riley Testut <[email protected] >> <mailto:[email protected]>> wrote: >> >> While I do believe your proposed additions have their benefits, my gut tells >> me this is too large a change to Swift for an arguably small gain. For this >> proposal, I'm wanting to keep the change as minimalistic as possible, while >> still providing enough flexibility and use cases to warrant it. > > I can definitely see the argument that extensibility is out of scope for > Swift 3, but I do want to make sure that it is possible for us to have > extensibility in the future (that we don’t block ourselves from doing it). > > I strongly disagree that the gain is small. One of the core benefits of both > Swift/ObjC (and now POP) is the ability to extend things post-hoc, without > access to the original code. > > I often write libraries & SDKs, so the users of my code don't have access to > the original code. I guess extensibility is less necessary when you control > the entire codebase, but you still have to refactor your factory whenever you > subclass (or adhere to a protocol), which I find problematic. > > This is something I run into a lot while writing actual code, so it isn’t > just a theoretical concern. > > > >> Interesting you bring up the registering of subclasses, as that is similar >> to the very original proposal, and actually similar to what I'm doing in my >> own app. Effectively, I have an Objective-C base class, and in +load it >> dynamically finds all subclasses using the Objective-C runtime, and stores a >> reference to them. Then in the initializer, it returns the appropriate >> subclass based on the initialization parameters. This was my solution to the >> superclass not having to explicitly know each individual subclass. >> >> Using factory initializers, however, this pattern could be used in pure >> Swift, albeit with some minor modifications. At program start, the program >> could register subclasses with the superclass, and then in the initializer >> would simply return the appropriate subclass (similar to NSURLProtocol, I >> believe). > > Yes, I have also used load in this way in ObjC. In Swift, I think the > compiler could easily build an array/table of the overloads of factory inits. > This avoids having to register explicitly (the declaration itself implies > intent to register), and doesn’t take up any cycles during execution. > > I ran into the problem of when to register the other day in a current > project, as Swift doesn’t have an equivalent to +load that I am aware of. I > had to settle for something of a hack which gets called on first use… but it > was only a partial solution which worked in that particular case. How do the > swift classes/enums/structs get called to register themselves? > > We may want to propose adding a +load equivalent for Swift types, since it > does come up occasionally. It is one of those things which you don’t need > often, but when you do, you really need it. Ironically, this was one of the > uses of the original feature I talked about earlier (we used it to provide > the equivalent of +load, awakeFromNib, etc… in the language). > > > >> As for rationale for protocol (factory) initializers, a common pattern I've >> come across when developing my internal frameworks is to design a protocol, >> and then to provide a default implementation of the protocol with a type >> (typically a struct). I feel this relationship could be bridged easier by >> simply returning this type from a protocol initializer, which could even >> potentially allow me to declare the actual type as private, so for all >> intents and purposes the client does simply get an initialized protocol >> object (similar in part to creating an anonymous class conforming to a >> protocol in Java). > > I strongly agree that we should have factory initializers for protocols for > the reasons you state here, plus many others. It just seems like a natural > fit. > > > I would like to see a 3 stage approach: > > Stage 1: Declaring an init as “factory” allows you to return any fully > inited object which fits the type (including calling self.init like a > convenience init would and then returning it). If the init is failable, you > can also return nil. This works both on classes and protocols. > > Stage 2: The compiler builds a table of the overrides of a given factory > init, and there is some syntax to call them (e.g. “let sub = > subclasses.init(value)”) such that the first one to return non-nil is the > return value to that call. Thorsten’s depth first + alphabetical ordering > would be adequate to make the behavior predictable. Again, this should work > for both classes and protocols. (I am open to better ideas for syntax/naming) > > Stage 3: We allow greater control over the ordering in the table. This still > needs thought both on syntax and method. Something similar to operator > precedence (or autolayout priority) would work in a pinch, but isn’t super > elegant. In practice, it might be enough just to be able to declare that > something needs to be before/after a specific subclass in the list. > > > Note: A slightly different approach to stage 2 (for classes) might have only > the direct subclasses in the table, and then the subclasses have the option > to call off to their own subclasses in the same manner if desired. This has > the tradeoff that each level needs to plan for extensibility (i.e. you can’t > override something which wasn’t expecting to be subclassed), but I do like > how it simplifies the model a bit. > > I have a feeling that the proper syntax for stages 2/3 may become more > obvious once the design work around mixins/protocols+storage gets done. It > may just fall out of the disambiguation syntax... > > Thanks, > Jon > >> On Mar 22, 2016, at 5:21 PM, Jonathan Hull <[email protected] >> <mailto:[email protected]>> wrote: >> >>> >>> Yes and No. >>> Yes, because this is a problem I run into all the time, and I really want >>> swift to have a solution for it. I especially like Brent’s idea of a >>> protocol init. >>> No, because I have gotten a bit greedy. I want us to take a step back and >>> look at the underlying problem to see if we can come up with something >>> which completely solves it… and I think factories get us only part way >>> there. Let’s take a moment and see if we can create something uniquely >>> swift before we copy/paste existing solutions. >>> Think about Cocoa’s class clusters for a moment. I love them, but they are >>> a pain to subclass. To the level where any beginning Cocoa instruction >>> tells you explicitly not to subclass them. Similarly, even in my own >>> factories, they are always a pain to extend. >>> I want our factory inits to be extensible by default. >>> >>> By extensible, I mean that I want to be able to add a new subclass (or a >>> new entity satisfying a protocol), and have it come out of the factory when >>> appropriate without editing the base class! Madness, I know, but: >>> 1) I may not have access to the source of the base class (e.g. Cocoa >>> Collections) >>> 2) I always feel a bit dirty giving the base class knowledge of it’s >>> subclasses >>> 3) It is a royal pain, and a potential source of errors as things get >>> refactored when adding new subclasses >>> >>> I think I have at least the seed of an idea of how to solve this, and I am >>> hoping that one of you (who are all much smarter than I) might have the key >>> to getting it the rest of the way. >>> I ran into this problem again last week, and it made me think of an old >>> language I used to use... >>> There was a small programming language I used to use in the 90’s which had >>> an interesting core language feature we called “handlers”. These were a lot >>> like registering for notifications, except that writing a function (with a >>> special “Handler” attribute: “Handler func myFuncName()") was all you >>> needed to do to register. Writing “Handle myFuncName()” would then >>> systematically call every function with that name and the Handler attribute. >>> That is, instead of calling a single function, it would systematically call >>> a series of functions (all with the same name). >>> There was one other thing that made these handlers special. Each one had >>> the option, when it was called, to reply that it was the one true handler, >>> and the others didn’t need to be called. Basically, it said “I've got >>> this!”. This even allowed it to return a result to the caller. >>> The original intent of this feature (and why it was a core language >>> feature) was to handle events. It would handle things like hit testing and >>> key events fairly elegantly. It was a powerful feature, so it was quickly >>> used for other things. It made things like plug-ins ridiculously simple. We >>> even used it for a form of error handling. >>> I remember helping to write a page layout program in it, and we used >>> handlers not just for the hit testing, but for the tool palette as well. >>> The end result was that you were able to add new shapes and new tools >>> without modifying existing code at all. It is a feature I miss all the >>> time... >>> >>> That is more power than we need here, but it provided me the inspiration >>> for a potential solution to the factory problem. Back to swift… >>> >>> The idea here is to give each interested subclass a chance to say “I've got >>> this!”. The factory init runs through each of the subclasses’ overrides >>> until it finds one that doesn’t return nil. New subclasses can be added and >>> they will be given a chance as well (without modifying the base class). The >>> first subclass to successfully init wins. (only subclasses which override >>> the factory init would be considered) >>> >>> class AbstractBase { >>> public factory init?(type: InformationToSwitchOn){ >>> //I like having an explicit call, so that the traditional >>> (non-distributed) factory is possible as well >>> return factory.init(type) //We could also call this >>> “subclass.init(type)” to mirror super >>> } >>> } >>> class ConcreteImplementation : AbstractBase { >>> public factory override init?(type: InformationToSwitchOn){ >>> guard type == compatibleWithThisType else {return nil} //If info >>> doesn’t work for us, we return nil, and the next class gets a shot >>> //Init concrete type here >>> } >>> } >>> >>> The main issue which still needs to be solved is that the order they get >>> called sometimes really matters (this was solved by a well defined ordering >>> + IDE features in the language mentioned above). For the most part, as long >>> as subclasses are called before their superclasses (or there is a some >>> method for cascading), it works. There are still times where you want to >>> define a specific ordering though (e.g. a new subclass wants to get called >>> before an existing subclasses to override some of it’s use cases). >>> I see a few options (and I would love to hear more): >>> - subclasses define a numeric precedence (similar to operators now). This >>> is probably the most effective stop-gap solution, but is not elegant. >>> - subclasses do whatever we change operator precedence to do in the future >>> - optionally allow subclasses to name another specific subclass that they >>> are before/after >>> - allow subclasses to declare that they would like to be earlier or later >>> (or that they don’t care) in the calling list. subclasses defined outside >>> of the module where the base class was defined would be placed more >>> extremely early/late. Exact order is undefined, but rough ordering is >>> possible. >>> - return (to the superclass) an array of all subclasses which successfully >>> inited. It can then select which one it wants and return it. This seems >>> overly inefficient to me, since you are initializing a bunch of unused >>> objects. >>> - <Your idea here> >>> >>> The end result is that you can extend factories of both classes and >>> protocols without access to the original source code. >>> >>> I also don’t think that the base should always need to be abstract. Factory >>> inits should allow returning subclasses as a way of replacing themselves >>> with a subclass, and returning nil if they are failable, but if there is no >>> return (or return self), it can create an instance of itself. This way, it >>> provides a customization point where subclasses can handle special cases, >>> but the class itself provides an obvious default (i.e. a much less messy >>> form of class cluster). >>> >>> class Base { >>> public factory init(type: InformationToSwitchOn){ >>> if let subclass = factory.init(type){ >>> return subclass >>> } >>> return self.init()//If subclasses didn’t work, initialize ourselves >>> } >>> } >>> >>> Thoughts? Too crazy to consider? >>> >>> Thanks, >>> Jon >>> >>> On Feb 8, 2016, at 11:26 AM, Charles Srstka <cocoadev at charlessoft.com >>> <https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote: >>> >>> >> On Dec 17, 2015, at 3:41 PM, Riley Testut via swift-evolution >>> >> <swift-evolution at swift.org >>> >> <https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote: >>> >> >>> >> Recently, I proposed the idea of adding the ability to implement the >>> >> "class cluster" pattern from Cocoa (Touch) in Swift. However, as we >>> >> discussed it and came up with different approaches, it evolved into a >>> >> functionality that I believe is far more beneficial to Swift, and >>> >> subsequently should be the focus of its own proposal. So here is the >>> >> improved (pre-)proposal: >>> >> >>> >> # Factory Initializers >>> >> >>> >> The "factory" pattern is common in many languages, including >>> >> Objective-C. Essentially, instead of initializing a type directly, a >>> >> method is called that returns an instance of the appropriate type >>> >> determined by the input parameters. Functionally this works well, but >>> >> ultimately it forces the client of the API to remember to call the >>> >> factory method instead, rather than the type's initializer. This might >>> >> seem like a minor gripe, but given that we want Swift to be as >>> >> approachable as possible to new developers, I think we can do better in >>> >> this regard. >>> >> >>> >> Rather than have a separate factory method, I propose we build the >>> >> factory pattern right into Swift, by way of specialized “factory >>> >> initializers”. The exact syntax was proposed by Philippe Hausler from >>> >> the previous thread, and I think it is an excellent solution: >>> >> >>> >> class AbstractBase { >>> >> public factory init(type: InformationToSwitchOn) { >>> >> return ConcreteImplementation(type) >>> >> } >>> >> } >>> >> >>> >> class ConcreteImplementation : AbstractBase { >>> >> >>> >> } >>> >> >>> >> Why exactly would this be useful in practice? In my own development, >>> >> I’ve come across a few places where this would especially be relevant: >>> >> >>> >> ## Class Cluster/Abstract Classes >>> >> This was the reasoning behind the original proposal, and I still think >>> >> it would be a very valid use case. The public superclass would declare >>> >> all the public methods, and could delegate off the specific >>> >> implementations to the private subclasses. Alternatively, this method >>> >> could be used as an easy way to handle backwards-compatibility: rather >>> >> than litter the code with branches depending on the OS version, simply >>> >> return the OS-appropriate subclass from the factory initializer. Very >>> >> useful. >>> >> >>> >> ## Protocol Initializers >>> >> Proposed by Brent Royal-Gordon, we could use factory initializers with >>> >> protocol extensions to return the appropriate instance conforming to a >>> >> protocol for the given needs. Similar to the class cluster/abstract >>> >> class method, but can work with structs too. This would be closer to the >>> >> factory method pattern, since you don’t need to know exactly what type >>> >> is returned, just the protocol it conforms to. >>> >> >>> >> ## Initializing Storyboard-backed View Controller >>> >> This is more specific to Apple Frameworks, but having factory >>> >> initializers could definitely help here. Currently, view controllers >>> >> associated with a storyboard must be initialized from the client through >>> >> a factory method on the storyboard instance (storyboard. >>> >> instantiateViewControllerWithIdentifier()). This works when the entire >>> >> flow of the app is storyboard based, but when a single storyboard is >>> >> used to configure a one-off view controller, having to initialize >>> >> through the storyboard is essentially use of private implementation >>> >> details; it shouldn’t matter whether the VC was designed in code or >>> >> storyboards, ultimately a single initializer should “do the right thing” >>> >> (just as it does when using XIBs directly). A factory initializer for a >>> >> View Controller subclass could handle the loading of the storyboard and >>> >> returning the appropriate view controller. >>> >> >>> >> Here are some comments from the previous thread that I believe are still >>> >> relevant: >>> >> >>> >> >>> >>> On Dec 9, 2015, at 1:06 PM, Philippe Hausler <phausler at apple.com >>> >>> <https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote: >>> >>> >>> >>> I can definitely attest that in implementing Foundation we could have >>> >>> much more idiomatic swift and much more similar behavior to the way >>> >>> Foundation on Darwin actually works if we had factory initializers. >>> >> >>> >> >>> >>> On Dec 7, 2015, at 5:24 PM, Brent Royal-Gordon <brent at >>> >>> architechies.com >>> >>> <https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote: >>> >>> >>> >>> A `protocol init` in a protocol extension creates an initializer which >>> >>> is *not* applied to types conforming to the protocol. Instead, it is >>> >>> actually an initializer on the protocol itself. `self` is the protocol >>> >>> metatype, not an instance of anything. The provided implementation >>> >>> should `return` an instance conforming to (and implicitly casted to) >>> >>> the protocol. Just like any other initializer, a `protocol init` can be >>> >>> failable or throwing. >>> >>> >>> >>> Unlike other initializers, Swift usually won’t be able to tell at >>> >>> compile time which concrete type will be returned by a protocol init(), >>> >>> reducing opportunities to statically bind methods and perform other >>> >>> optimization tricks. Frankly, though, that’s just the cost of doing >>> >>> business. If you want to select a type dynamically, you’re going to >>> >>> lose the ability to aggressively optimize calls to the resulting >>> >>> instance. >>> >> >>> >> >>> >> I’d love to hear everyone’s thoughts on this! >>> >> >>> >> Best, >>> >> Riley Testut >>> >> _______________________________________________ >>> >> swift-evolution mailing list >>> >> swift-evolution at swift.org >>> >> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>> >> https://lists.swift.org/mailman/listinfo/swift-evolution >>> >> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>> > >>> > Was any proposal for this ever written up? It would be really useful to >>> > have, and it appeared to have the support of several Apple staff members. >>> > >>> > Charles >>> >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
