[swift-evolution] Factory initializers

Claus Ruete clausruete at icloud.com
Tue Jan 19 11:47:37 CST 2016


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(_:) 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! 😊

C.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160119/144e0cba/attachment.html>


More information about the swift-evolution mailing list