[swift-evolution] [Completing Generics] Opening existentials — A use case: type-safe dependency injection container

Adam Sharp adsharp at me.com
Sat Mar 5 01:15:00 CST 2016


> One explicit way to allow such operations in a type-safe manner is to introduce an “open existential” operation of some sort, which extracts and gives a name to the dynamic type stored inside an existential. For example:
> 
> 	if let storedInE1 = e1 openas T { // T is a the type of storedInE1, a copy of the value stored in e1
> 	  if let storedInE2 = e2 as? T { // is e2 also a T?
> 	    if storedInE1 == storedInE2 { … } // okay: storedInT1 and storedInE2 are both of type T, which we know is Equatable
> 	  }
> 	}

If I'm not mistaken, this topic was in the "maybe" section, and I'd like to offer a concrete use case.

A while back I attempted to extend `UIStoryboard` to be a type-safe dependency injection container. For example, given a protocol like this:

	protocol Presenter: class {
	  associatedtype Data // Don't worry too much about this name, it's probably overly generic

	  // Called by the dependency injection container after initialisation to inject the presented data.
	  func updateWithData(data: Data)
	}

And then a view controller could declare itself to be a `Presenter`:

	class ProfileViewController: Presenter {
	  typealias Data = Profile

	  private var profile: Profile?

	  func updateWithData(profile: Profile) {
	    self.profile = profile
	    updateDisplay()
	  }
	}

And then when setting up your storyboard, you would register various factory closures to fetch this data on demand:

	let storyboard = DIStoryboard(name: "Profile", registerDependencies: { dependencies in
	  dependencies.add { () -> Profile in
	    return profileCache.currentUserProfile
	  }
	})

To glue these pieces together, you'd need a way to declare an existential type for the current view controller, and then match that against one of the registered factory functions:

	// DIStoryboard.swift
	private func updateViewController(viewController: UIViewController) {
	  if let <P: Presenter> presenter = viewController as? P {
	    // Now what? Possibly:
	    // 1. Iterate over each factory function
	    // 2. When I find a function () -> P.Data, call it and pass the result to viewController.updateWithData()
	  }
	}

Is this a good idea, in general? I don't really know, because I couldn't really complete the experiment!

But I have a feeling that there might be other cases where opening existentials could allow for more type-safe code when wrapping UIKit functionality. Currently, Swift generics are completely incompatible with the way objects are unarchived from storyboards and nibs, and it feels like this feature _could_ help to bridge the gap. I have a feeling a lot of Swift users would like to bring more of Swift's type safety to their Cocoa code.

Anyway, I'd love to hear some feedback about this idea!

–Adam


More information about the swift-evolution mailing list