Actually you cannot do this even if NumericFoo is public because NumericFoo would be an undeclared type in this case.
But Why?
For Example, I understand that the following is wrong:
func fp<T: P>() -> T {
....
}
let t = fp()
Because the type of the t cannot be inferred.
However in this case, the type of Bar.FooType is settled in this case, and it is NumericFoo<NumericType>. So if we pass a Double in that initializer:
public extension Bar {
init<NumericType: Numeric>(_ number: NumericType) {
let foo = NumericFoo(value: number) // <== The type of the `foo` will *NOT* be generic but a specific one: NumericFoo<Double>
self.init(foo: foo) // <== This will be like `self.init(foo: FooType) where FooType == NumericFoo<Double>`
}
}
And because of that, the following function will work and you can try:
func test<N: Numeric>(_ n: N) {
let foo = NumericFoo(value: n)
let bar = Bar(foo: foo)
print(bar.value)
}
test(Double.pi) // print 3.141592653589793
Yes, I do need the associatedtype to keep the wrapped value for later use.
What I am trying to do is to provide an API that deals with different kind of types, and is able to refer to its original value.
public protocol FooProtocol {
associatedtype ValueType
var value: ValueType { get }
func doSomething()
}
And internally, we provides implementations for all kinds of types we support:
internal struct NumericFoo<T: Numeric>: FooProtocol {
let value: T
func doSomething() { ... }
}
internal struct StringFoo<T: StringProtocol>: FooProtocol {
let value: T
func doSomething() { ... }
}
internal struct NumericRawFoo<T: RawRepresentable>: FooProtocol where T.RawValue: Numeric {
let value: T
func doSomething() { ... }
}
internal struct StringRawFoo<T: RawRepresentable>: FooProtocol where T.RawValue: StringProtocol {
let value: T
func doSomething() { ... }
}
internal struct NumericCollectionFoo<T: Collection>: FooProtocol where T.Element: Numeric {
let value: T
func doSomething() { ... }
}
internal struct StringCollectionFoo<T: Collection>: FooProtocol where T.Element: StringProtocol {
let value: T
func doSomething() { ... }
}
/// and more...
And for the external use, a wrapper type is provided with different initializers:
public struct Bar<FooType: FooProtocol>: FooProtocol {
private let underlying: FooType
internal init(foo: FooType) {
self.underlying = foo
}
public var value: FooType.ValueType { return self.underlying.value }
public func doSomething() { self.underlying.doSomething() }
}
And initializers for different type:
public extension Bar {
init<T: Numeric>(_ value: T) {
let foo = NumericFoo(value: value)
self.init(foo: foo)
}
init<T: StringProtocol>(_ value: T) {
let foo = StringFoo(value: value)
self.init(foo: foo)
}
init<T: RawRepresentable>(_ value: T) where T.RawValue: Numeric {
let foo = NumericRawFoo(value: value)
self.init(foo: foo)
}
init<T: RawRepresentable>(_ value: T) where T.RawValue: StringProtocol {
let foo = StringRawFoo(value: value)
self.init(foo: foo)
}
init<T: Collection>(_ value: T) where T.Element: Numeric {
let foo = NumericCollectionFoo(value: value)
self.init(foo: foo)
}
init<T: StringProtocol>(_ value: T) where T.Element: StringProtocol {
let foo = StringCollectionFoo(value: value)
self.init(foo: foo)
}
/// and more...
}