[swift-evolution] Proposal: Allow class cluster pattern via dynamic initializer return type

Riley Testut rileytestut at gmail.com
Mon Dec 7 20:03:27 CST 2015


Brent, I agree we shouldn’t be focused on Objective-C interop. In fact, this proposal was designed to copy over a pattern implemented in Objective-C, but to then come up with a “swifty” solution. I don’t think dealing with classes necessarily defeats this goal? Sure, Swift has structs too, but classes still play a big role in the language.

Besides that, I like the idea of a protocol-based approach. I don’t necessarily see the need for a class-specific init as you mentioned at the end, because the returned values for the protocols could still be subclasses of a base class. Of course, I’m working with only assumptions right now about when this is useful; all I know is given my current situation, a protocol-based initializer, as you suggested will work well. The only benefit I see would be to not have to create a separate protocol (which isn’t that hard to do), so if we had to stick with just one option, I think you nailed a great implementation.

Additionally, I’m neutral towards whether we should assign self directly in the proposed “protocol init”, or return a type; both seem like good solutions to me.

> On Dec 7, 2015, at 3:39 PM, ilya <ilya.nikokoshev at gmail.com> wrote:
> 
> Some thoughts: 
> 
> 1 "self =" is better in inits because of a common pattern of configuring an object in the second init stage 
> 
> 2 since there is already an existing syntax for protocol implementation functions, the role of init keyword is basically to stand for "empty function name" syntax. This sounds interesting. 
> 
> 3 you can also add init to the protocol (with Self return value and required keyword) for some interesting effects. 
> 
> 4 final init, not class init (because init is already called on the class):
> 
> class BaseClass {
> init(x: ...) -> Self // regular, inheritable init, Self can be omitted 
> final init(y:...) -> Self // Self always = BaseClass, can return Derived()
> 
> 5 yes, that would nicely work with ObjC, even if that was not the original goal :)
> 
> On Tue, Dec 8, 2015 at 02:24 Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> > Throughout its frameworks, Apple makes use of the “class cluster” pattern as a means to separate the public API out from the (potentially complex) internal representations of the data. Clients of the API simply use the public API, while under the hood a different implementation is chosen to most efficiently represent the provided initialization parameter values.
> >
> > Unfortunately, because initializers in Swift are not methods like in Objective-C, there is no way to specify what the actual return value should be (short of returning nil for failable initializers). This makes it *impossible* to actually implement the class cluster pattern in Swift.
> 
> I’d actually like to put Objective-C interop aside for a moment. There are many class clusters in Objective-C, but most of them are things you rarely need to subclass yourself. When was the last time you wrote an NSArray subclass? What’s more interesting to me is how we can provide a similar native Swift pattern.
> 
> Here’s my thinking.
> 
> First of all, we call them “class” clusters, but there’s no reason this should be only for classes. A “class cluster” is basically a single API which offers access to many different implementations of an interface. The phrase “many different implementations of an interface” is a hint that we should be thinking about protocols.
> 
> Here’s what I think Swift should support:
> 
>         protocol HTTPParameterListType {
>                 var all: DictionaryLiteral<String, Strong> { get }
>                 subscript (name: String) -> [String] { get }
>         }
> 
>         // Various implementations include:
>         struct JSONParameterList: HTTPParameterList {...}
>         struct MultipartParameterList: HTTParameterList {…}
>         struct URLEncodedFormParameterList: HTTPParameterList {…}
>         struct QueryParameterList: HTTPParameterList {…}
> 
>         extension HTTPParameterListType {
>                 protocol init(request: NSHTTPRequest, bodyData: NSData) throws {
>                         switch request.valueForHTTPHeaderField(“Content-Type”).map({ MIMEType(rawValue: $0) }) {
>                         case .JSON?:
>                                 return try JSONParameterList(data: bodyData)
> 
>                         case .MultipartFormData?:
>                                 return try MultipartParameterList(data: bodyData)
> 
>                         case .URLEncodedForm?:
>                                 return try URLEncodedFormParameterList(data: bodyData)
> 
>                         default:
>                                 return try QueryParameterList(request: request)
>                         }
>                 }
>         }
> 
>         // Usage:
>         self.parameters = HTTPParameterListType(request: request, bodyData: data)
> 
> 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.
> 
> Perhaps this feature can then be extended back to classes, with a `class init` being an un-inherited initializer which uses `return` to give the caller a new object. `self` would be the class object, and only class variables and methods would be callable from it. But like I said, I’m not particularly interested in Objective-C interop right now.
> 
> --
> Brent Royal-Gordon
> Architechies
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151207/76b04d24/attachment.html>


More information about the swift-evolution mailing list