Proposal: Allow class cluster pattern via dynamic initializer return type


(Riley Testut) #1

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (
https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md).
But for convenience, I’ve included it in this email body as well. Hopefully
someone else thinks this would be an idea worth considering :-)

*## Introduction*

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.

*## Motivation*

While developing my own Swift framework, I found myself wanting to provide
a similar functionality. For the client of said framework, I wanted them to
be able to create an instance of an essentially abstract class, and be
returned a private subclass instance suited best for handling whatever
input initialization parameters they provided. It didn’t make sense given
the circumstances to ask the user to decide which class would be the best
representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility
significantly easier; instead of littering your code with branches for
different versions of an OS, you could instead have one if/switch statement
to determine the appropriate subclass for the current OS you’re running on.
This allows the developer to trivially keep legacy code for older platforms
while taking advantage of new APIs/designs, and also without changing *any*
client code. An example of the class cluster pattern being used for this
reason can be seen here:
http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

*## Proposed solution*

I propose that we allow for implementation of the class cluster pattern by
providing a way to (at run time) specify the actual type that should be
initialized depending on the provided initialization parameters.

*## Detailed design*

*Introduce a new class method that can return an appropriate type that
should be used for initialization, depending on the provided initialization
parameters.*

This is what I believe to be the most clean solution, and with (assumedly)
minimal impact on the existing nature of Swift’s initialization process. To
ensure this remains safe, the only types allowed to be returned should be
subclasses of the parent class (such as returning a __NSArrayI for
NSArray). Notably, beyond this method, everything else remains the same;
all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:

*## Impact on existing code*

There will be zero impact on existing code; if the proposed class method is
not implemented, then it will default to simply initializing the “base”
class, as it always has.

*## Alternatives considered*

*Allow for return values in initializers*

This is essentially how most class cluster patterns are implemented in
Objective-C. Inside the init method, the class inspects the provided
parameters, then assigns self to an instance of the appropriate subclass.
Unfortunately, this is wasteful; memory is allocated for the base class,
and then subsequently replaced with new memory allocated for the
appropriate base class. More importantly though, the whole process can be
complicated; it can be very easy to make an infinite recursive loop by
calling [super init] in the subclass, which then assigns self to a new
instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to
implement.

*Class function to return appropriate instance*

This is probably the simplest approach: simply make a class function that
returns an instance of the appropriate class given a few input parameters.
This totally works, but it means consumers of the API have to remember to
use the class method instead of the initializer. Even if all initializers
for the class were marked private, it would be strange to have the
dissonance between using initializers and class methods to instantiate
types in code. The consumer should not have to know about *any* of the
implementation details; everything should “just work”. Forcing them to use
alternative means to instantiate objects breaks this philosophy, IMO.

*Derive from Objective-C base class*

Another option is to simply derive from an Objective-C base class, and this
is actually what I am doing right now in my framework. Unfortunately, there
is one significant drawback: because the initialization is happening in
Objective-C, you can only provide Objective-C compatible types for the
initialization parameters (so no Swift structs for you!). Additionally,
this (obviously) means whatever code is using it is limited to systems with
Objective-C support, so it is not as portable as a pure-Swift solution.


(David Owens II) #2

Well, the basic idea of a class cluster is to hide the internal implementation. The caller of the API is still supposed to get back an instance of the clustered type (or that conforms to the interface at least).

It’s still possible to create class clusters though; here’s an example playground:

private protocol _Cluster {
    func description() -> String
}

class Cluster {
    
    private var _instance: _Cluster
    
    init(name: String) {
        _instance = _ClusterString(name: name)
    }
    
    init(value: Int) {
        _instance = _ClusterValue(value: value)
    }
    
    func description() -> String {
        return _instance.description()
    }
}

private class _ClusterString: _Cluster {
    private var name: String
    init(name: String) { self.name = name }
    func description() -> String {
        return "_ClusterString: \(name)"
    }
}

private class _ClusterValue: _Cluster {
    private var value: Int
    init(value: Int) { self.value = value }
    func description() -> String {
        return "_ClusterValue: \(value)"
    }
}

let s = Cluster(name: "a string")
s.description()

let v = Cluster(value: 12)
v.description()

The implementation is different from how ObjC implements class clusters, but the end result is nearly identical in functionality. If Swift had a form of function redirection, this pattern could be supported with less boiler-plate. However, I don’t believe this proposal is necessary to support class clusters.

-David

···

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution <swift-evolution@swift.org> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :slight_smile:

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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


(Brent Royal-Gordon) #3

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


(Ling Wang) #4

Why not use factory methods?

···

On Mon, Dec 7, 2015 at 2:19 PM Riley Testut via swift-evolution < swift-evolution@swift.org> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (
https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md).
But for convenience, I’ve included it in this email body as well. Hopefully
someone else thinks this would be an idea worth considering :slight_smile:

*## Introduction*

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.

*## Motivation*

While developing my own Swift framework, I found myself wanting to provide
a similar functionality. For the client of said framework, I wanted them to
be able to create an instance of an essentially abstract class, and be
returned a private subclass instance suited best for handling whatever
input initialization parameters they provided. It didn’t make sense given
the circumstances to ask the user to decide which class would be the best
representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility
significantly easier; instead of littering your code with branches for
different versions of an OS, you could instead have one if/switch statement
to determine the appropriate subclass for the current OS you’re running on.
This allows the developer to trivially keep legacy code for older platforms
while taking advantage of new APIs/designs, and also without changing *any*
client code. An example of the class cluster pattern being used for this
reason can be seen here:
http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

*## Proposed solution*

I propose that we allow for implementation of the class cluster pattern by
providing a way to (at run time) specify the actual type that should be
initialized depending on the provided initialization parameters.

*## Detailed design*

*Introduce a new class method that can return an appropriate type that
should be used for initialization, depending on the provided initialization
parameters.*

This is what I believe to be the most clean solution, and with (assumedly)
minimal impact on the existing nature of Swift’s initialization process. To
ensure this remains safe, the only types allowed to be returned should be
subclasses of the parent class (such as returning a __NSArrayI for
NSArray). Notably, beyond this method, everything else remains the same;
all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

*## Impact on existing code*

There will be zero impact on existing code; if the proposed class method
is not implemented, then it will default to simply initializing the “base”
class, as it always has.

*## Alternatives considered*

*Allow for return values in initializers*

This is essentially how most class cluster patterns are implemented in
Objective-C. Inside the init method, the class inspects the provided
parameters, then assigns self to an instance of the appropriate subclass.
Unfortunately, this is wasteful; memory is allocated for the base class,
and then subsequently replaced with new memory allocated for the
appropriate base class. More importantly though, the whole process can be
complicated; it can be very easy to make an infinite recursive loop by
calling [super init] in the subclass, which then assigns self to a new
instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to
implement.

*Class function to return appropriate instance*

This is probably the simplest approach: simply make a class function that
returns an instance of the appropriate class given a few input parameters.
This totally works, but it means consumers of the API have to remember to
use the class method instead of the initializer. Even if all initializers
for the class were marked private, it would be strange to have the
dissonance between using initializers and class methods to instantiate
types in code. The consumer should not have to know about *any* of the
implementation details; everything should “just work”. Forcing them to use
alternative means to instantiate objects breaks this philosophy, IMO.

*Derive from Objective-C base class*

Another option is to simply derive from an Objective-C base class, and
this is actually what I am doing right now in my framework. Unfortunately,
there is one significant drawback: because the initialization is happening
in Objective-C, you can only provide Objective-C compatible types for the
initialization parameters (so no Swift structs for you!). Additionally,
this (obviously) means whatever code is using it is limited to systems with
Objective-C support, so it is not as portable as a pure-Swift solution.

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


(Philippe Hausler) #5

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.

It would be interesting to simplify the syntax down to something along the lines of

class AbstractBase {
    public factory init(type: InformationToSwitchOn) {
        return ConcreteImplementation(type)
    }
}

class ConcreteImplementation : AbstractBase {

}

This way the initialization would be returned from a standard init method and the return value is inherited correctly. The one potential issue with this is that structs would not fit well into this since the layout size must be known.

···

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution <swift-evolution@swift.org> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :slight_smile:

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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


(Riley Testut) #6

Personally, I think enforcing factory methods is a way of leaking implementation details; there is no reason why the consumer of the API should have to question whether they should use an initializer or a factory method, because they shouldn't need to think about it at all. Especially in my case, this is used quite frequently, so having to remember to use a factory method for all necessary types is just annoying.

Imagine if NSString used factory methods instead of initializers. It'd be far less convenient, because you'd have to mentally note that you *can't* initialize that class directly. Blech.

···

On Dec 7, 2015, at 12:30 PM, an0 <an00na@gmail.com> wrote:

Why not use factory methods?

On Mon, Dec 7, 2015 at 2:19 PM Riley Testut via swift-evolution <swift-evolution@swift.org> wrote:
Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :-)

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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


(Austin Zheng) #7

I really like the idea of having a 'protocol initializer' which
transparently chooses an appropriate concrete type to return. Once
recursive protocol conformance and conditional conformance has been
implemented, you'd be able to work with type "aggregates" (imagine JSON or
plist types implemented exclusively as protocol conformances on existing
types) almost completely in terms of the generic protocol interface.

Austin

···

On Mon, Dec 7, 2015 at 3:24 PM, Brent Royal-Gordon via swift-evolution < swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jacob Bandes-Storch) #8

To me it seems a simpler solution would be to allow `return`ing a different
object (or assigning to self) from inside init.

···

On Mon, Dec 7, 2015 at 12:55 PM David Owens II via swift-evolution < swift-evolution@swift.org> wrote:

Well, the basic idea of a class cluster is to hide the internal
implementation. The caller of the API is still supposed to get back an
instance of the clustered type (or that conforms to the interface at least).

It’s still possible to create class clusters though; here’s an example
playground:

private protocol _Cluster {
    func description() -> String
}

class Cluster {

    private var _instance: _Cluster

    init(name: String) {
        _instance = _ClusterString(name: name)
    }

    init(value: Int) {
        _instance = _ClusterValue(value: value)
    }

    func description() -> String {
        return _instance.description()
    }
}

private class _ClusterString: _Cluster {
    private var name: String
    init(name: String) { self.name = name }
    func description() -> String {
        return "_ClusterString: \(name)"
    }
}

private class _ClusterValue: _Cluster {
    private var value: Int
    init(value: Int) { self.value = value }
    func description() -> String {
        return "_ClusterValue: \(value)"
    }
}

let s = Cluster(name: "a string")
s.description()

let v = Cluster(value: 12)
v.description()

The implementation is different from how ObjC implements class clusters,
but the end result is nearly identical in functionality. If Swift had a
form of function redirection, this pattern could be supported with less
boiler-plate. However, I don’t believe this proposal is necessary to
support class clusters.

-David

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution < > swift-evolution@swift.org> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (
https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md).
But for convenience, I’ve included it in this email body as well. Hopefully
someone else thinks this would be an idea worth considering :slight_smile:

*## Introduction*

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.

*## Motivation*

While developing my own Swift framework, I found myself wanting to provide
a similar functionality. For the client of said framework, I wanted them to
be able to create an instance of an essentially abstract class, and be
returned a private subclass instance suited best for handling whatever
input initialization parameters they provided. It didn’t make sense given
the circumstances to ask the user to decide which class would be the best
representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility
significantly easier; instead of littering your code with branches for
different versions of an OS, you could instead have one if/switch statement
to determine the appropriate subclass for the current OS you’re running on.
This allows the developer to trivially keep legacy code for older platforms
while taking advantage of new APIs/designs, and also without changing *any*
client code. An example of the class cluster pattern being used for this
reason can be seen here:
http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

*## Proposed solution*

I propose that we allow for implementation of the class cluster pattern by
providing a way to (at run time) specify the actual type that should be
initialized depending on the provided initialization parameters.

*## Detailed design*

*Introduce a new class method that can return an appropriate type that
should be used for initialization, depending on the provided initialization
parameters.*

This is what I believe to be the most clean solution, and with (assumedly)
minimal impact on the existing nature of Swift’s initialization process. To
ensure this remains safe, the only types allowed to be returned should be
subclasses of the parent class (such as returning a __NSArrayI for
NSArray). Notably, beyond this method, everything else remains the same;
all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

*## Impact on existing code*

There will be zero impact on existing code; if the proposed class method
is not implemented, then it will default to simply initializing the “base”
class, as it always has.

*## Alternatives considered*

*Allow for return values in initializers*

This is essentially how most class cluster patterns are implemented in
Objective-C. Inside the init method, the class inspects the provided
parameters, then assigns self to an instance of the appropriate subclass.
Unfortunately, this is wasteful; memory is allocated for the base class,
and then subsequently replaced with new memory allocated for the
appropriate base class. More importantly though, the whole process can be
complicated; it can be very easy to make an infinite recursive loop by
calling [super init] in the subclass, which then assigns self to a new
instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to
implement.

*Class function to return appropriate instance*

This is probably the simplest approach: simply make a class function that
returns an instance of the appropriate class given a few input parameters.
This totally works, but it means consumers of the API have to remember to
use the class method instead of the initializer. Even if all initializers
for the class were marked private, it would be strange to have the
dissonance between using initializers and class methods to instantiate
types in code. The consumer should not have to know about *any* of the
implementation details; everything should “just work”. Forcing them to use
alternative means to instantiate objects breaks this philosophy, IMO.

*Derive from Objective-C base class*

Another option is to simply derive from an Objective-C base class, and
this is actually what I am doing right now in my framework. Unfortunately,
there is one significant drawback: because the initialization is happening
in Objective-C, you can only provide Objective-C compatible types for the
initialization parameters (so no Swift structs for you!). Additionally,
this (obviously) means whatever code is using it is limited to systems with
Objective-C support, so it is not as portable as a pure-Swift solution.

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

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


(ilya) #9

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 :slight_smile:

···

On Tue, Dec 8, 2015 at 02:24 Brent Royal-Gordon via swift-evolution < swift-evolution@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #10

I actually like the way it's done in the Objective-C, where _isa pointer
will be changed appropriately.

Perhaps it's actually possible to solve this problem:

Unfortunately, this is wasteful; memory is allocated for the base class,

and then subsequently replaced with new memory allocated for the
appropriate base class. More importantly though, the whole process can be
complicated;

by noting that we can formally mark this initializer as convenience
initializer, and memory allocation doesn't need to happen until we hit a
designated initializer. So far I see no reason why this cannot be made to
work:

class Cluster {

    convenience init(parameters) {
        if stuff {
            self = _Cluster1(...)
        } else {
            self = _Cluster2(...)
        }

        // it's safe to continue with second init phase
        self.configure(...)
   }
}

class _Cluster1 {
    init(parameters: ...) { ... } // designated init, guaranteed never to
call convenience inits
}

···

On Mon, Dec 7, 2015 at 11:55 PM, David Owens II via swift-evolution < swift-evolution@swift.org> wrote:

Well, the basic idea of a class cluster is to hide the internal
implementation. The caller of the API is still supposed to get back an
instance of the clustered type (or that conforms to the interface at least).

It’s still possible to create class clusters though; here’s an example
playground:

private protocol _Cluster {
    func description() -> String
}

class Cluster {

    private var _instance: _Cluster

    init(name: String) {
        _instance = _ClusterString(name: name)
    }

    init(value: Int) {
        _instance = _ClusterValue(value: value)
    }

    func description() -> String {
        return _instance.description()
    }
}

private class _ClusterString: _Cluster {
    private var name: String
    init(name: String) { self.name = name }
    func description() -> String {
        return "_ClusterString: \(name)"
    }
}

private class _ClusterValue: _Cluster {
    private var value: Int
    init(value: Int) { self.value = value }
    func description() -> String {
        return "_ClusterValue: \(value)"
    }
}

let s = Cluster(name: "a string")
s.description()

let v = Cluster(value: 12)
v.description()

The implementation is different from how ObjC implements class clusters,
but the end result is nearly identical in functionality. If Swift had a
form of function redirection, this pattern could be supported with less
boiler-plate. However, I don’t believe this proposal is necessary to
support class clusters.

-David

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution < > swift-evolution@swift.org> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (
https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md).
But for convenience, I’ve included it in this email body as well. Hopefully
someone else thinks this would be an idea worth considering :slight_smile:

*## Introduction*

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.

*## Motivation*

While developing my own Swift framework, I found myself wanting to provide
a similar functionality. For the client of said framework, I wanted them to
be able to create an instance of an essentially abstract class, and be
returned a private subclass instance suited best for handling whatever
input initialization parameters they provided. It didn’t make sense given
the circumstances to ask the user to decide which class would be the best
representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility
significantly easier; instead of littering your code with branches for
different versions of an OS, you could instead have one if/switch statement
to determine the appropriate subclass for the current OS you’re running on.
This allows the developer to trivially keep legacy code for older platforms
while taking advantage of new APIs/designs, and also without changing *any*
client code. An example of the class cluster pattern being used for this
reason can be seen here:
http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

*## Proposed solution*

I propose that we allow for implementation of the class cluster pattern by
providing a way to (at run time) specify the actual type that should be
initialized depending on the provided initialization parameters.

*## Detailed design*

*Introduce a new class method that can return an appropriate type that
should be used for initialization, depending on the provided initialization
parameters.*

This is what I believe to be the most clean solution, and with (assumedly)
minimal impact on the existing nature of Swift’s initialization process. To
ensure this remains safe, the only types allowed to be returned should be
subclasses of the parent class (such as returning a __NSArrayI for
NSArray). Notably, beyond this method, everything else remains the same;
all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

*## Impact on existing code*

There will be zero impact on existing code; if the proposed class method
is not implemented, then it will default to simply initializing the “base”
class, as it always has.

*## Alternatives considered*

*Allow for return values in initializers*

This is essentially how most class cluster patterns are implemented in
Objective-C. Inside the init method, the class inspects the provided
parameters, then assigns self to an instance of the appropriate subclass.
Unfortunately, this is wasteful; memory is allocated for the base class,
and then subsequently replaced with new memory allocated for the
appropriate base class. More importantly though, the whole process can be
complicated; it can be very easy to make an infinite recursive loop by
calling [super init] in the subclass, which then assigns self to a new
instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to
implement.

*Class function to return appropriate instance*

This is probably the simplest approach: simply make a class function that
returns an instance of the appropriate class given a few input parameters.
This totally works, but it means consumers of the API have to remember to
use the class method instead of the initializer. Even if all initializers
for the class were marked private, it would be strange to have the
dissonance between using initializers and class methods to instantiate
types in code. The consumer should not have to know about *any* of the
implementation details; everything should “just work”. Forcing them to use
alternative means to instantiate objects breaks this philosophy, IMO.

*Derive from Objective-C base class*

Another option is to simply derive from an Objective-C base class, and
this is actually what I am doing right now in my framework. Unfortunately,
there is one significant drawback: because the initialization is happening
in Objective-C, you can only provide Objective-C compatible types for the
initialization parameters (so no Swift structs for you!). Additionally,
this (obviously) means whatever code is using it is limited to systems with
Objective-C support, so it is not as portable as a pure-Swift solution.

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

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


(Jean-Daniel) #11

My experience with class cluster, is that they are confusing and almost implemented wrong. You can as easily create a « class cluster » simply be not exposing the constructor of your class and providing factory methods instead. While it may have some drawback in the Obj-C days (class methods are used to returned autoreleased objects), that point no longer apply in modern obj-c (using ARC) and swift code.

For instance, instead of having NSNumber(float:0), NSNumber(int:0) you can just have NSNumber.float(0), and NSNumber.int(0) with float and int being class or static method returning instance of subclasses of NSNumber.

I don’t see what is the benefit of having a class cluster designed by providing fake initialization method vs using a factory method.

···

Le 9 déc. 2015 à 20:06, Philippe Hausler via swift-evolution <swift-evolution@swift.org> a écrit :

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.

It would be interesting to simplify the syntax down to something along the lines of

class AbstractBase {
    public factory init(type: InformationToSwitchOn) {
        return ConcreteImplementation(type)
    }
}

class ConcreteImplementation : AbstractBase {

}

This way the initialization would be returned from a standard init method and the return value is inherited correctly. The one potential issue with this is that structs would not fit well into this since the layout size must be known.

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :slight_smile:

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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

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


(Riley Testut) #12

Glad you mentioned the Foundation port, because I was curious how the lack of true class cluster support would affect that project. Nice to see some validation that it would be a useful feature :-)

I really like the proposed syntax; I think it is the simplest to both implement and understand of the solutions in this thread. But as you said, it wouldn’t work for structs, which while personally is not a major factor for my project, I agree if we’re adding a new feature to the language, it might seem strange to limit it to classes because of a technicality like that.

Maybe the AbstractBase of a “cluster" of structs could be implemented similarly to indirect enums? I’m no compiler guru by any stretch of the imagination, but if it is known that the AbstractBase will never be used directly, maybe it’s easier to “fake” it then? I definitely want whatever solution decided on (if it’s decided that this is a worthwhile feature) to work for both value and reference types. Additionally, if we can pull this off, maybe we could extend the “public factory init” syntax to protocols as well, so either a reference or a value type could be returned from a protocol’s factory init method (implemented through a protocol extension), though I don’t think that needs to be as high priority.

···

On Dec 9, 2015, at 11:06 AM, Philippe Hausler <phausler@apple.com> 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.

It would be interesting to simplify the syntax down to something along the lines of

class AbstractBase {
    public factory init(type: InformationToSwitchOn) {
        return ConcreteImplementation(type)
    }
}

class ConcreteImplementation : AbstractBase {

}

This way the initialization would be returned from a standard init method and the return value is inherited correctly. The one potential issue with this is that structs would not fit well into this since the layout size must be known.

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :-)

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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


(David Owens II) #13

You still need everything else if can return arbitrary types from init(). The only thing you don’t get from Swift today is the forwarding that is required for the _Cluster protocol. However, that problem is a more general one that has been discussed by many outside of the swift-evolution (surprised there is no proposal for that yet).

If forwarding was in the language, there is no difference between the implementations other than the requirement of the _instance variable.

···

On Dec 7, 2015, at 12:59 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

To me it seems a simpler solution would be to allow `return`ing a different object (or assigning to self) from inside init.

On Mon, Dec 7, 2015 at 12:55 PM David Owens II via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Well, the basic idea of a class cluster is to hide the internal implementation. The caller of the API is still supposed to get back an instance of the clustered type (or that conforms to the interface at least).

It’s still possible to create class clusters though; here’s an example playground:

private protocol _Cluster {
    func description() -> String
}

class Cluster {
    
    private var _instance: _Cluster
    
    init(name: String) {
        _instance = _ClusterString(name: name)
    }
    
    init(value: Int) {
        _instance = _ClusterValue(value: value)
    }
    
    func description() -> String {
        return _instance.description()
    }
}

private class _ClusterString: _Cluster {
    private var name: String
    init(name: String) { self.name <http://self.name/> = name }
    func description() -> String {
        return "_ClusterString: \(name)"
    }
}

private class _ClusterValue: _Cluster {
    private var value: Int
    init(value: Int) { self.value = value }
    func description() -> String {
        return "_ClusterValue: \(value)"
    }
}

let s = Cluster(name: "a string")
s.description()

let v = Cluster(value: 12)
v.description()

The implementation is different from how ObjC implements class clusters, but the end result is nearly identical in functionality. If Swift had a form of function redirection, this pattern could be supported with less boiler-plate. However, I don’t believe this proposal is necessary to support class clusters.

-David

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :slight_smile:

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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

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


(Riley Testut) #14

Worth starting another thread discussing the pros and cons of method forwarding in Swift? I think that would definitely solve the class cluster problem

···

On Dec 7, 2015, at 1:06 PM, David Owens II <david@owensd.io> wrote:

You still need everything else if can return arbitrary types from init(). The only thing you don’t get from Swift today is the forwarding that is required for the _Cluster protocol. However, that problem is a more general one that has been discussed by many outside of the swift-evolution (surprised there is no proposal for that yet).

If forwarding was in the language, there is no difference between the implementations other than the requirement of the _instance variable.

On Dec 7, 2015, at 12:59 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

To me it seems a simpler solution would be to allow `return`ing a different object (or assigning to self) from inside init.

On Mon, Dec 7, 2015 at 12:55 PM David Owens II via swift-evolution <swift-evolution@swift.org> wrote:

Well, the basic idea of a class cluster is to hide the internal implementation. The caller of the API is still supposed to get back an instance of the clustered type (or that conforms to the interface at least).

It’s still possible to create class clusters though; here’s an example playground:

private protocol _Cluster {
    func description() -> String
}

class Cluster {
    
    private var _instance: _Cluster
    
    init(name: String) {
        _instance = _ClusterString(name: name)
    }
    
    init(value: Int) {
        _instance = _ClusterValue(value: value)
    }
    
    func description() -> String {
        return _instance.description()
    }
}

private class _ClusterString: _Cluster {
    private var name: String
    init(name: String) { self.name = name }
    func description() -> String {
        return "_ClusterString: \(name)"
    }
}

private class _ClusterValue: _Cluster {
    private var value: Int
    init(value: Int) { self.value = value }
    func description() -> String {
        return "_ClusterValue: \(value)"
    }
}

let s = Cluster(name: "a string")
s.description()

let v = Cluster(value: 12)
v.description()

The implementation is different from how ObjC implements class clusters, but the end result is nearly identical in functionality. If Swift had a form of function redirection, this pattern could be supported with less boiler-plate. However, I don’t believe this proposal is necessary to support class clusters.

-David

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution <swift-evolution@swift.org> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :-)

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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

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


(Riley Testut) #15

I actually really like the idea of using a convenience initializer for this, it works well with the Swift initiation process. +1 (though maybe a different keyword than "convenience" to show that it actually assigns to self?)

···

On Dec 7, 2015, at 1:07 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

I actually like the way it's done in the Objective-C, where _isa pointer will be changed appropriately.

Perhaps it's actually possible to solve this problem:

> Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated;

by noting that we can formally mark this initializer as convenience initializer, and memory allocation doesn't need to happen until we hit a designated initializer. So far I see no reason why this cannot be made to work:

class Cluster {

    convenience init(parameters) {
        if stuff {
            self = _Cluster1(...)
        } else {
            self = _Cluster2(...)
        }

        // it's safe to continue with second init phase
        self.configure(...)
   }
}

class _Cluster1 {
    init(parameters: ...) { ... } // designated init, guaranteed never to call convenience inits
}

On Mon, Dec 7, 2015 at 11:55 PM, David Owens II via swift-evolution <swift-evolution@swift.org> wrote:
Well, the basic idea of a class cluster is to hide the internal implementation. The caller of the API is still supposed to get back an instance of the clustered type (or that conforms to the interface at least).

It’s still possible to create class clusters though; here’s an example playground:

private protocol _Cluster {
    func description() -> String
}

class Cluster {
    
    private var _instance: _Cluster
    
    init(name: String) {
        _instance = _ClusterString(name: name)
    }
    
    init(value: Int) {
        _instance = _ClusterValue(value: value)
    }
    
    func description() -> String {
        return _instance.description()
    }
}

private class _ClusterString: _Cluster {
    private var name: String
    init(name: String) { self.name = name }
    func description() -> String {
        return "_ClusterString: \(name)"
    }
}

private class _ClusterValue: _Cluster {
    private var value: Int
    init(value: Int) { self.value = value }
    func description() -> String {
        return "_ClusterValue: \(value)"
    }
}

let s = Cluster(name: "a string")
s.description()

let v = Cluster(value: 12)
v.description()

The implementation is different from how ObjC implements class clusters, but the end result is nearly identical in functionality. If Swift had a form of function redirection, this pattern could be supported with less boiler-plate. However, I don’t believe this proposal is necessary to support class clusters.

-David

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution <swift-evolution@swift.org> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :-)

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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

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


(Riley Testut) #16

So instead of chaining convenience initializers to required ones via self.init(), you’d recommend instead assigning them directly to self? I think this would be a nice change; currently it can be hard to know the correct method of chaining initializers (at least as far as I’ve seen when attempting to explain it to newcomers).

Proposed new rules for initialization:

required initializers: must call super (unless base class)
convenience initializers: must assign to self via a required initializer

I think this also would help with the confusion of why convenience methods can’t call super, but required ones can, since now convenience methods can’t chain to any initializers directly. Thoughts from others?

···

On Dec 7, 2015, at 1:17 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

more precisely like this for regular convenience initializers

class SomeClass {

    convenience init(x: Int) {
        let y = someComputation(x) // <- can't use self here
        self = init(y: y)
        self.configure() // <- can use self here
    }
     
    init(y:Int) { // designated
    ...
    }
}

On Tue, Dec 8, 2015 at 12:14 AM, ilya <ilya.nikokoshev@gmail.com <mailto:ilya.nikokoshev@gmail.com>> wrote:
Actually I think self = SomeClass(anotherInit: ...) would be a better syntax of choice for *all* convenience initializers, as it would make intuitively clear that self cannot be used until this call.

On Tue, Dec 8, 2015 at 12:11 AM, Riley Testut <rileytestut@gmail.com <mailto:rileytestut@gmail.com>> wrote:
I actually really like the idea of using a convenience initializer for this, it works well with the Swift initiation process. +1 (though maybe a different keyword than "convenience" to show that it actually assigns to self?)

On Dec 7, 2015, at 1:07 PM, ilya <ilya.nikokoshev@gmail.com <mailto:ilya.nikokoshev@gmail.com>> wrote:

I actually like the way it's done in the Objective-C, where _isa pointer will be changed appropriately.

Perhaps it's actually possible to solve this problem:

> Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated;

by noting that we can formally mark this initializer as convenience initializer, and memory allocation doesn't need to happen until we hit a designated initializer. So far I see no reason why this cannot be made to work:

class Cluster {

    convenience init(parameters) {
        if stuff {
            self = _Cluster1(...)
        } else {
            self = _Cluster2(...)
        }

        // it's safe to continue with second init phase
        self.configure(...)
   }
}

class _Cluster1 {
    init(parameters: ...) { ... } // designated init, guaranteed never to call convenience inits
}

On Mon, Dec 7, 2015 at 11:55 PM, David Owens II via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Well, the basic idea of a class cluster is to hide the internal implementation. The caller of the API is still supposed to get back an instance of the clustered type (or that conforms to the interface at least).

It’s still possible to create class clusters though; here’s an example playground:

private protocol _Cluster {
    func description() -> String
}

class Cluster {
    
    private var _instance: _Cluster
    
    init(name: String) {
        _instance = _ClusterString(name: name)
    }
    
    init(value: Int) {
        _instance = _ClusterValue(value: value)
    }
    
    func description() -> String {
        return _instance.description()
    }
}

private class _ClusterString: _Cluster {
    private var name: String
    init(name: String) { self.name <http://self.name/> = name }
    func description() -> String {
        return "_ClusterString: \(name)"
    }
}

private class _ClusterValue: _Cluster {
    private var value: Int
    init(value: Int) { self.value = value }
    func description() -> String {
        return "_ClusterValue: \(value)"
    }
}

let s = Cluster(name: "a string")
s.description()

let v = Cluster(value: 12)
v.description()

The implementation is different from how ObjC implements class clusters, but the end result is nearly identical in functionality. If Swift had a form of function redirection, this pattern could be supported with less boiler-plate. However, I don’t believe this proposal is necessary to support class clusters.

-David

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :-)

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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

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


(Riley Testut) #17

So instead of chaining convenience initializers to required ones via self.init(), you’d recommend instead assigning them directly to self? I think this would be a nice change; currently it can be hard to know the correct method of chaining initializers (at least as far as I’ve seen when attempting to explain it to newcomers).

Proposed new rules for initialization:

required initializers: must call super (unless base class)
convenience initializers: must assign to self via a required initializer

I think this also would help with the confusion of why convenience methods can’t call super, but required ones can, since now convenience methods can’t chain to any initializers directly. Thoughts from others?

···

On Dec 7, 2015, at 1:17 PM, ilya <ilya.nikokoshev@gmail.com <mailto:ilya.nikokoshev@gmail.com>> wrote:

more precisely like this for regular convenience initializers

class SomeClass {

    convenience init(x: Int) {
        let y = someComputation(x) // <- can't use self here
        self = init(y: y)
        self.configure() // <- can use self here
    }
     
    init(y:Int) { // designated
    ...
    }
}

On Tue, Dec 8, 2015 at 12:14 AM, ilya <ilya.nikokoshev@gmail.com <mailto:ilya.nikokoshev@gmail.com>> wrote:
Actually I think self = SomeClass(anotherInit: ...) would be a better syntax of choice for *all* convenience initializers, as it would make intuitively clear that self cannot be used until this call.

On Tue, Dec 8, 2015 at 12:11 AM, Riley Testut <rileytestut@gmail.com <mailto:rileytestut@gmail.com>> wrote:
I actually really like the idea of using a convenience initializer for this, it works well with the Swift initiation process. +1 (though maybe a different keyword than "convenience" to show that it actually assigns to self?)

On Dec 7, 2015, at 1:07 PM, ilya <ilya.nikokoshev@gmail.com <mailto:ilya.nikokoshev@gmail.com>> wrote:

I actually like the way it's done in the Objective-C, where _isa pointer will be changed appropriately.

Perhaps it's actually possible to solve this problem:

> Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated;

by noting that we can formally mark this initializer as convenience initializer, and memory allocation doesn't need to happen until we hit a designated initializer. So far I see no reason why this cannot be made to work:

class Cluster {

    convenience init(parameters) {
        if stuff {
            self = _Cluster1(...)
        } else {
            self = _Cluster2(...)
        }

        // it's safe to continue with second init phase
        self.configure(...)
   }
}

class _Cluster1 {
    init(parameters: ...) { ... } // designated init, guaranteed never to call convenience inits
}

On Mon, Dec 7, 2015 at 11:55 PM, David Owens II via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Well, the basic idea of a class cluster is to hide the internal implementation. The caller of the API is still supposed to get back an instance of the clustered type (or that conforms to the interface at least).

It’s still possible to create class clusters though; here’s an example playground:

private protocol _Cluster {
    func description() -> String
}

class Cluster {
    
    private var _instance: _Cluster
    
    init(name: String) {
        _instance = _ClusterString(name: name)
    }
    
    init(value: Int) {
        _instance = _ClusterValue(value: value)
    }
    
    func description() -> String {
        return _instance.description()
    }
}

private class _ClusterString: _Cluster {
    private var name: String
    init(name: String) { self.name <http://self.name/> = name }
    func description() -> String {
        return "_ClusterString: \(name)"
    }
}

private class _ClusterValue: _Cluster {
    private var value: Int
    init(value: Int) { self.value = value }
    func description() -> String {
        return "_ClusterValue: \(value)"
    }
}

let s = Cluster(name: "a string")
s.description()

let v = Cluster(value: 12)
v.description()

The implementation is different from how ObjC implements class clusters, but the end result is nearly identical in functionality. If Swift had a form of function redirection, this pattern could be supported with less boiler-plate. However, I don’t believe this proposal is necessary to support class clusters.

-David

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on GitHub (https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md). But for convenience, I’ve included it in this email body as well. Hopefully someone else thinks this would be an idea worth considering :-)

## Introduction

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.

## Motivation

While developing my own Swift framework, I found myself wanting to provide a similar functionality. For the client of said framework, I wanted them to be able to create an instance of an essentially abstract class, and be returned a private subclass instance suited best for handling whatever input initialization parameters they provided. It didn’t make sense given the circumstances to ask the user to decide which class would be the best representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards compatibility significantly easier; instead of littering your code with branches for different versions of an OS, you could instead have one if/switch statement to determine the appropriate subclass for the current OS you’re running on. This allows the developer to trivially keep legacy code for older platforms while taking advantage of new APIs/designs, and also without changing *any* client code. An example of the class cluster pattern being used for this reason can be seen here: http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

## Proposed solution

I propose that we allow for implementation of the class cluster pattern by providing a way to (at run time) specify the actual type that should be initialized depending on the provided initialization parameters.

## Detailed design

Introduce a new class method that can return an appropriate type that should be used for initialization, depending on the provided initialization parameters.

This is what I believe to be the most clean solution, and with (assumedly) minimal impact on the existing nature of Swift’s initialization process. To ensure this remains safe, the only types allowed to be returned should be subclasses of the parent class (such as returning a __NSArrayI for NSArray). Notably, beyond this method, everything else remains the same; all this does is change what class the initializer is called on initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

## Impact on existing code

There will be zero impact on existing code; if the proposed class method is not implemented, then it will default to simply initializing the “base” class, as it always has.

## Alternatives considered

Allow for return values in initializers

This is essentially how most class cluster patterns are implemented in Objective-C. Inside the init method, the class inspects the provided parameters, then assigns self to an instance of the appropriate subclass. Unfortunately, this is wasteful; memory is allocated for the base class, and then subsequently replaced with new memory allocated for the appropriate base class. More importantly though, the whole process can be complicated; it can be very easy to make an infinite recursive loop by calling [super init] in the subclass, which then assigns self to a new instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to implement.

Class function to return appropriate instance

This is probably the simplest approach: simply make a class function that returns an instance of the appropriate class given a few input parameters. This totally works, but it means consumers of the API have to remember to use the class method instead of the initializer. Even if all initializers for the class were marked private, it would be strange to have the dissonance between using initializers and class methods to instantiate types in code. The consumer should not have to know about *any* of the implementation details; everything should “just work”. Forcing them to use alternative means to instantiate objects breaks this philosophy, IMO.

Derive from Objective-C base class

Another option is to simply derive from an Objective-C base class, and this is actually what I am doing right now in my framework. Unfortunately, there is one significant drawback: because the initialization is happening in Objective-C, you can only provide Objective-C compatible types for the initialization parameters (so no Swift structs for you!). Additionally, this (obviously) means whatever code is using it is limited to systems with Objective-C support, so it is not as portable as a pure-Swift solution.

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

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


(Riley Testut) #18

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@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Riley Testut) #19

While I agree this solves the issue, I feel this enhancement could be useful for far more than replicating the simple class cluster pattern. Because of this, I think might make a new proposal focusing on protocol initializers, and have the class cluster pattern be just one example of an advantage. Are there any other problems this might be able to solve?

···

On Dec 7, 2015, at 6:17 PM, Austin Zheng <austinzheng@gmail.com> wrote:

I really like the idea of having a 'protocol initializer' which transparently chooses an appropriate concrete type to return. Once recursive protocol conformance and conditional conformance has been implemented, you'd be able to work with type "aggregates" (imagine JSON or plist types implemented exclusively as protocol conformances on existing types) almost completely in terms of the generic protocol interface.

Austin

On Mon, Dec 7, 2015 at 3:24 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #20

Yes, that's what I suggest:

*required initializers*: must call super (autogenerated for base class)
*convenience initializers*: must assign to self via another initializer
(including via subclass)

(with the minor technical correction that convenience initializers, as is
now, are able to call other convenience initializers)

Although note that inheritance rules for "class cluster" initializers will
be very different, the reason being that regular convenience init is
essentially

init(x...) -> Self {
   self = Self(y: ...)
}

which is covariant enough to be able to be inherited, while "class cluster
init" is

init (x...) -> BaseClass {
    self = SomeConcreteClass(y: ...)
}

which is not covariant.

···

On Tue, Dec 8, 2015 at 1:06 AM, Riley Testut <rileytestut@gmail.com> wrote:

So instead of chaining convenience initializers to required ones via
self.init(), you’d recommend instead assigning them directly to self? I
think this would be a nice change; currently it can be hard to know the
correct method of chaining initializers (at least as far as I’ve seen when
attempting to explain it to newcomers).

Proposed new rules for initialization:

required initializers: must call super (unless base class)
convenience initializers: must assign to self via a required initializer

I think this also would help with the confusion of why convenience methods
can’t call super, but required ones can, since now convenience methods
can’t chain to *any* initializers directly. Thoughts from others?

On Dec 7, 2015, at 1:17 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

more precisely like this for regular convenience initializers

class SomeClass {

    convenience init(x: Int) {
        let y = someComputation(x) // <- can't use self here
        self = init(y: y)
        self.configure() // <- can use self here
    }

    init(y:Int) { // designated
    ...
    }
}

On Tue, Dec 8, 2015 at 12:14 AM, ilya <ilya.nikokoshev@gmail.com> wrote:

Actually I think self = SomeClass(anotherInit: ...) would be a better
syntax of choice for *all* convenience initializers, as it would make
intuitively clear that self cannot be used until this call.

On Tue, Dec 8, 2015 at 12:11 AM, Riley Testut <rileytestut@gmail.com> >> wrote:

I actually really like the idea of using a convenience initializer for
this, it works well with the Swift initiation process. +1 (though maybe a
different keyword than "convenience" to show that it actually assigns to
self?)

On Dec 7, 2015, at 1:07 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

I actually like the way it's done in the Objective-C, where _isa pointer
will be changed appropriately.

Perhaps it's actually possible to solve this problem:

> Unfortunately, this is wasteful; memory is allocated for the base
class, and then subsequently replaced with new memory allocated for the
appropriate base class. More importantly though, the whole process can be
complicated;

by noting that we can formally mark this initializer as convenience
initializer, and memory allocation doesn't need to happen until we hit a
designated initializer. So far I see no reason why this cannot be made to
work:

class Cluster {

    convenience init(parameters) {
        if stuff {
            self = _Cluster1(...)
        } else {
            self = _Cluster2(...)
        }

        // it's safe to continue with second init phase
        self.configure(...)
   }
}

class _Cluster1 {
    init(parameters: ...) { ... } // designated init, guaranteed never
to call convenience inits
}

On Mon, Dec 7, 2015 at 11:55 PM, David Owens II via swift-evolution < >>> swift-evolution@swift.org> wrote:

Well, the basic idea of a class cluster is to hide the internal
implementation. The caller of the API is still supposed to get back an
instance of the clustered type (or that conforms to the interface at least).

It’s still possible to create class clusters though; here’s an example
playground:

private protocol _Cluster {
    func description() -> String
}

class Cluster {

    private var _instance: _Cluster

    init(name: String) {
        _instance = _ClusterString(name: name)
    }

    init(value: Int) {
        _instance = _ClusterValue(value: value)
    }

    func description() -> String {
        return _instance.description()
    }
}

private class _ClusterString: _Cluster {
    private var name: String
    init(name: String) { self.name = name }
    func description() -> String {
        return "_ClusterString: \(name)"
    }
}

private class _ClusterValue: _Cluster {
    private var value: Int
    init(value: Int) { self.value = value }
    func description() -> String {
        return "_ClusterValue: \(value)"
    }
}

let s = Cluster(name: "a string")
s.description()

let v = Cluster(value: 12)
v.description()

The implementation is different from how ObjC implements class
clusters, but the end result is nearly identical in functionality. If Swift
had a form of function redirection, this pattern could be supported with
less boiler-plate. However, I don’t believe this proposal is necessary to
support class clusters.

-David

On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution < >>>> swift-evolution@swift.org> wrote:

Happy Monday everyone!

I wrote up a prototype proposal, which is probably best viewed on
GitHub (
https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md).
But for convenience, I’ve included it in this email body as well. Hopefully
someone else thinks this would be an idea worth considering :slight_smile:

*## Introduction*

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.

*## Motivation*

While developing my own Swift framework, I found myself wanting to
provide a similar functionality. For the client of said framework, I wanted
them to be able to create an instance of an essentially abstract class, and
be returned a private subclass instance suited best for handling whatever
input initialization parameters they provided. It didn’t make sense given
the circumstances to ask the user to decide which class would be the best
representation of the data; it should “just work”.

Additionally, the class cluster pattern can make backwards
compatibility significantly easier; instead of littering your code with
branches for different versions of an OS, you could instead have one
if/switch statement to determine the appropriate subclass for the current
OS you’re running on. This allows the developer to trivially keep legacy
code for older platforms while taking advantage of new APIs/designs, and
also without changing *any* client code. An example of the class cluster
pattern being used for this reason can be seen here:
http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/

*## Proposed solution*

I propose that we allow for implementation of the class cluster pattern
by providing a way to (at run time) specify the actual type that should be
initialized depending on the provided initialization parameters.

*## Detailed design*

*Introduce a new class method that can return an appropriate type that
should be used for initialization, depending on the provided initialization
parameters.*

This is what I believe to be the most clean solution, and with
(assumedly) minimal impact on the existing nature of Swift’s initialization
process. To ensure this remains safe, the only types allowed to be returned
should be subclasses of the parent class (such as returning a __NSArrayI
for NSArray). Notably, beyond this method, everything else remains the
same; all this does is change what class the initializer is called on
initially.

Here is an ideal implementation gist:
https://gist.github.com/rileytestut/0e6e80d3f22b845502e7

*## Impact on existing code*

There will be zero impact on existing code; if the proposed class
method is not implemented, then it will default to simply initializing the
“base” class, as it always has.

*## Alternatives considered*

*Allow for return values in initializers*

This is essentially how most class cluster patterns are implemented in
Objective-C. Inside the init method, the class inspects the provided
parameters, then assigns self to an instance of the appropriate subclass.
Unfortunately, this is wasteful; memory is allocated for the base class,
and then subsequently replaced with new memory allocated for the
appropriate base class. More importantly though, the whole process can be
complicated; it can be very easy to make an infinite recursive loop by
calling [super init] in the subclass, which then assigns self to a new
instance of the subclass, which then calls [super init]…etc.

tl;dr; this method would work, but would be somewhat inconvenient to
implement.

*Class function to return appropriate instance*

This is probably the simplest approach: simply make a class function
that returns an instance of the appropriate class given a few input
parameters. This totally works, but it means consumers of the API have to
remember to use the class method instead of the initializer. Even if all
initializers for the class were marked private, it would be strange to have
the dissonance between using initializers and class methods to instantiate
types in code. The consumer should not have to know about *any* of the
implementation details; everything should “just work”. Forcing them to use
alternative means to instantiate objects breaks this philosophy, IMO.

*Derive from Objective-C base class*

Another option is to simply derive from an Objective-C base class, and
this is actually what I am doing right now in my framework. Unfortunately,
there is one significant drawback: because the initialization is happening
in Objective-C, you can only provide Objective-C compatible types for the
initialization parameters (so no Swift structs for you!). Additionally,
this (obviously) means whatever code is using it is limited to systems with
Objective-C support, so it is not as portable as a pure-Swift solution.

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

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