Questions are:
- how to type-erase a PAT (a Protocol with Associated-Type) that exposes an init method? The PAT being
SpecializedContribution
and my tentative beingAnySpecializedContribution
in the code example below. - how to type-erase a PAT that exposes the metatype of the associatedtype? The PAT being
SpecializedContributionConfiguration
and my tentative beingAnySpecializedContributionConfiguration
in the code example below.
I realized this is 2 questions instead of 1 but I have the intuition that they are tied together and could both be resolved by playing with metatypes.
The idea of the code below is to allow sub-modules to vend "contribution configurations" to the main-app module but to let the main-app module actually init the contributions. This is notable useful to enforce, at compile time, that sub-modules don't cheat and rely on contribution singletons internally.
public protocol Contributing {
associatedtype Dependencies
init(dependencies: Dependencies)
}
public protocol ContributionConfigurating {
associatedtype Contribution: Contributing
var type: Contribution.Type { get }
var dependencies: Contribution.Dependencies { get }
}
public protocol SpecializedContribution: Contributing {
var anAPI: Int { get }
}
public protocol SpecializedContributionConfiguration: ContributionConfigurating where Contribution: SpecializedContribution {}
// If I build a "configuration provider" that returns a single configuration, it works like a charm
// thanks to opaques types. But I want the provider to return multiple configurations.
public protocol SingleSpecializedContributionConfigurationProviding {
associatedtype ConcreteContributionConfiguration: SpecializedContributionConfiguration
func contributionConfiguration() -> ConcreteContributionConfiguration
}
class SingleSpecializedContributionConfigurationProvider: SingleSpecializedContributionConfigurationProviding {
public func contributionConfiguration() -> some SpecializedContributionConfiguration {
// <code that actually returns a concrete implementation of a SpecializedContributionConfiguration>
}
}
// First attempt, just return the protocol. This doesn't work, a classic array-with-PATs issue.
// No surprise so far.
public protocol MultipleSpecializedContributionConfigurationsProviding {
func contributionConfigurations() -> [SpecializedContributionConfiguration]
}
// Second (and current) attempt, type-erase the configurations.
// Problems?
// 1/ The configuration protocol vends a metatype.
// 2/ The contribution protocol, which I'd have to type-erase eventually, exposes an init method.
public protocol TypeErasedMultipleSpecializedContributionConfigurationsProviding {
associatedtype ConcreteContributionConfiguration: SpecializedContributionConfiguration
func contributionConfigurations() -> [ConcreteContributionConfiguration]
}
class TypeErasedMultipleSpecializedContributionConfigurationsProvider: TypeErasedMultipleSpecializedContributionConfigurationsProviding {
public func contributionConfigurations() -> [AnySpecializedContributionConfiguration] {
[
AnySpecializedContributionConfiguration(
// Let's assume this is defined somewhere
FooContributionConfiguration()
),
AnySpecializedContributionConfiguration(
// Let's assume this is also defined somewhere
BarContributionConfiguration()
)
]
}
}
public struct AnySpecializedContributionConfiguration: SpecializedContributionConfiguration {
public typealias Contribution = AnySpecializedContribution
public var type: Contribution.Type {
// I don't know what to store here.
AnyContribution.self
}
public var dependencies: Contribution.Dependencies {
_dependencies()
}
private let _dependencies: () -> Contribution.Dependencies
init<T: SpecializedContributionConfiguration>(_ configuration: T) {
_dependencies = {
configuration.dependencies
}
}
}
public struct AnySpecializedContribution: SpecializedContribution {
public typealias Dependencies = Any
private let _anAPI: () -> Int
public var anAPI: Int {
_anAPI()
}
init<T: SpecializedContribution>(_ contribution: T) {
_anAPI = {
contribution.anAPI
}
}
// How to "type-erase the Contributing.init(dependencies: Dependencies)"?
}