protocol FactoryAType { | |
typealias Product | |
} | |
protocol FactoryBType { | |
typealias Product | |
} | |
struct CombinedFactory<T: FactoryAType, U: FactoryBType where T.Product == U.Product> { | |
let factoryA: T | |
let factoryB: U? | |
init(factoryA: T, factoryB: U? = nil) { | |
self.factoryA = factoryA | |
self.factoryB = factoryB | |
} | |
} | |
struct IntAFactory: FactoryAType { | |
typealias Product = Int | |
} | |
struct IntBFactory: FactoryBType { | |
typealias Product = Int | |
} | |
let fa = IntAFactory() | |
let fb = IntBFactory() | |
let allGood = CombinedFactory(factoryA: fa, factoryB: fb) | |
// Does not work: | |
// Cannot invoke initializer for type 'CombinedFactory<_, _>' with an argument list of type '(factoryA: IntAFactory)' | |
let combinedFactory = CombinedFactory(factoryA: fa) | |
// This works: | |
// But this is awkward. I don't want to have to specify B if it is nil | |
let workingFactory = CombinedFactory<IntAFactory, IntBFactory>(factoryA: fa) |
In most cases, if U
is not passed, then this shouldn't be a CombinedFactory
. It should just an AFactory
. Just make sure that AFactory
and CombinedFactory
conform to the same protocol so they can be used interchangeably. It'd be helpful to see the actual use case; my experience is that when you find yourself here, the mistake happened much earlier and you shouldn't even have been here. Most common cause is using a struct and methods when you could have just used a closure and avoided all the intermediate types.
If you really need the idea of "no such U
" then you need to say so:
struct EmptyBFactory<A: FactoryAType>: FactoryBType {
typealias Product = A.Product
}
Then you can use it like:
let onlya = CombinedFactory(factoryA: factoryA, factoryB: EmptyBFactory<FactoryAType>())
or create a function like:
func aOnly<A: FactoryAType>(factoryA: A) -> CombinedFactory<A, EmptyBFactory<A>> {
return CombinedFactory(factoryA: factoryA, factoryB: EmptyBFactory<A>())
}
At this point, however, you do slam into numerous weaknesses in the compiler. You can't make aOnly
into a init
. You can't even make it into a class static
. Swift just doesn't have the ability to have init
explicitly provide its associated types (I don't believe there's any deep reason for that to be impossible, but it is).
I'm very interested in what the underlying problem is, though. "Factory" is already sounding like something un-Swifty.
my experience is that when you find yourself here, the mistake happened much earlier and you shouldn't even have been here.
This is sooooo truuuuuue
Updated my playing around here: https://gist.github.com/rnapier/44bf23300959cca9c43f
Thanks guys!
So the full context is https://github.com/jessesquires/JSQDataSourcesKit.
"Factory" is already sounding like something un-Swifty.
Ha -- probably because I'm trying to make Cocoa less cubersome. I hate that UICollectionView/UITableView delegates/dataSources are littered with @optional
methods
So
CombinedFactory
has 2 generic type parameter that must satisfyT.Product == U.Product
.However,
U
is optional, which makes this expression weird:CombinedFactory<IntAFactory, IntBFactory>(factoryA: fa)
It would be nicer if
CombinedFactory(factoryA: fa)
worked instead.Is there a better way to express these types or another (better?) way to achieve this?