Factory initializers


(Claus Ruete) #1

Hi!

I hope this is the right mailing list for this: I want to make a proposal for the swift language. This is not in the commonly rejected changes and i also haven't found it anywhere else, so hopefully I am doing this right.

What I would like to see in swift is a better way of implementing "factory methods" that return an instance of a class, but unlike normal initializers, may return an instance of a subclass instead of just the class itself.

Let me explain one important use case why i think this would be a good idea. Imagine we define a protocol requiring an initializer like this:

protocol StringLoadable {
init(string: String)
}

And then we want a class "Car", that has a subclass "BigCar", to conform to this protocol.

class Car: StringLoadable {
required convenience init(string: String) {
if string.characters.count < 10 {
self.init()
}
else {
let bigCar = BigCar()

     //\.\.\. oops, how do i return this now?
  \}

}
}

class BigCar: Car {
//...
}

So the Car should be a BigCar in case the string contains 10 characters or more. Now the only way i could see this possibly work is to use a static fun instead of an initializer:

class Car: StringLoadable {
static func loadFromString(string: String) -> Car {
if string.characters.count < 10 {
return Car()
}
else {
return BigCar()
}
}
}

This works, but only until we try to have this as a protocol requirement:

protocol StringLoadable {
static func loadFromString(string: String) -> Self
}

While i already dislike that the whole protocol (and thus, the API) have to change just because of an implementation detail in one class, the even bigger problem is that for non-final classes, it is impossible to implement this required static function: Having it return "Car" does not satisfy the protocol, because we end up in a situation where BigCar.loadFromString(_:slight_smile: returns a Car instead of a BigCar.

Factory initializers would fix this problem:

protocol StringLoadable {
init(string: String)
}

class Car: StringLoadable {
required factory init(string: String) {
if string.characters.count < 10 {
return Car()
}
else {
return BigCar(string: string)
}
}

init() { }
}

class BigCar: Car {
required factory init(string: String) {
return BigCar()
}

init() { }
}

In this case, the factory init is marked as "required" for protocol conformance, but in other use cases it doesn't have to be required. A factory init would act mostly like a convenience init, with the exception of being able to initialize a subclass or load an object from somewhere else, for example a cache or something like that.

A factory initializer would never allocate any memory itself, that work happens in the actual designated initializer. Also, like a convenience initializer, a class cannot only have a factory initializer, but needs at least one designated initializer. Because of this, factory initializers would not cause any problems in terms of being able to access uninitialized properties or something like that.

I hope you agree that factory initializers would be a good addition to swift. As shown in the example, they allow things to be implemented that weren't before, and they don't cause any problems with type safety or any other Swift concepts.

Here are some more ideas on factory initializers:

We could have factory initializers in protocol extensions: In some cases, this would allow to completely hide the actual implementations of that protocol – by being able to "construct" a protocol.

In terms of the syntax, instead of using a "return" pattern, the "assign to self" pattern, as known from enum initializers, could be used, but i think using self in a factory initializer could cause confusion (The compiler would have to prevent "self" from being used before it was assigned a value), so i prefer the "return" pattern with no access to "self" at all.

Thank you all for reading! :blush:

C.


(Andrew Bennett) #2

Hi Claus,

It looks like you've put a lot of thought and consideration into how this
could fit into Swift :slight_smile:

There was early proposals for class clusters that turned into a discussion
on factory initialisers. You'll probably find a lot of shared goals and
ideas here:

http://comments.gmane.org/gmane.comp.lang.swift.evolution/17

It would be great once you've read through that thread to contribute your
ideas to it!

Andrew

···

On Wed, Jan 20, 2016 at 4:47 AM, Claus Ruete via swift-evolution < swift-evolution@swift.org> wrote:

Hi!

I hope this is the right mailing list for this: I want to make a proposal
for the swift language. This is not in the commonly rejected changes and i
also haven't found it anywhere else, so hopefully I am doing this right.

What I would like to see in swift is a better way of implementing "factory
methods" that return an instance of a class, but unlike normal
initializers, may return an instance of a subclass instead of just the
class itself.

Let me explain one important use case why i think this would be a good
idea. Imagine we define a protocol requiring an initializer like this:

protocol StringLoadable {
   init(string: String)
}

And then we want a class "Car", that has a subclass "BigCar", to conform
to this protocol.

class Car: StringLoadable {
   required convenience init(string: String) {
      if string.characters.count < 10 {
         self.init()
      }
      else {
         let bigCar = BigCar()

         //... oops, how do i return this now?
      }
   }
}

class BigCar: Car {
   //...
}

So the Car should be a BigCar in case the string contains 10 characters or
more. Now the only way i could see this possibly work is to use a static
fun instead of an initializer:

class Car: StringLoadable {
   static func loadFromString(string: String) -> Car {
      if string.characters.count < 10 {
         return Car()
      }
      else {
         return BigCar()
      }
   }
}

This works, but only until we try to have this as a protocol requirement:

protocol StringLoadable {
   static func loadFromString(string: String) -> Self
}

While i already dislike that the whole protocol (and thus, the API) have
to change just because of an implementation detail in one class, the even
bigger problem is that for non-final classes, it is impossible to implement
this required static function: Having it return "Car" does not satisfy the
protocol, because we end up in a situation where BigCar.loadFromString(_:slight_smile:
returns a Car instead of a BigCar.

Factory initializers would fix this problem:

protocol StringLoadable {
   init(string: String)
}

class Car: StringLoadable {
   required *factory* init(string: String) {
      if string.characters.count < 10 {
         return Car()
      }
      else {
         return BigCar(string: string)
      }
   }

   init() { }
}

class BigCar: Car {
   required *factory* init(string: String) {
      return BigCar()
   }

   init() { }
}

In this case, the factory init is marked as "required" for protocol
conformance, but in other use cases it doesn't have to be required. A
factory init would act mostly like a convenience init, with the exception
of being able to initialize a subclass or load an object from somewhere
else, for example a cache or something like that.

A factory initializer would never allocate any memory itself, that work
happens in the actual designated initializer. Also, like a convenience
initializer, a class cannot only have a factory initializer, but needs at
least one designated initializer. Because of this, factory initializers
would not cause any problems in terms of being able to access uninitialized
properties or something like that.

I hope you agree that factory initializers would be a good addition to
swift. As shown in the example, they allow things to be implemented that
weren't before, and they don't cause any problems with type safety or any
other Swift concepts.

Here are some more ideas on factory initializers:

We could have factory initializers in protocol extensions: In some cases,
this would allow to completely hide the actual implementations of that
protocol – by being able to "construct" a protocol.

In terms of the syntax, instead of using a "return" pattern, the "assign
to self" pattern, as known from enum initializers, could be used, but i
think using self in a factory initializer could cause confusion (The
compiler would have to prevent "self" from being used before it was
assigned a value), so i prefer the "return" pattern with no access to
"self" at all.

Thank you all for reading! :blush:

C.

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