This is unfortunately a limitation of protocols. Alternatively, you could use a value witness type, like so:
struct MyCustomString<A> {
var inString: (A) -> String
}
extension MyCustomString where A == Int {
static let int = Self { String($0) } // alternatively Self(String.init)
}
extension MyCustomString where A == Double {
static let double = Self { String($0) }
}
extension MyCustomString where A == String {
static let string = Self { $0 }
}
extension MyCustomString where A: Collection {
static func collection<B>(of witness: MyCustomString<B>) -> Self where A.Element == B {
return Self { collection in
collection
.map { witness.inString($0) }
.joined(separator: ", ")
}
}
}
func printCustomString<A>(value: A, type witness: MyCustomString<A>) {
print(witness.inString(value))
}
printCustomString(value: 2.0, type: .double)
// 2.0
printCustomString(value: [1, 2, 3, 4, 5], type: .collection(of: .int))
// 1, 2, 3, 4, 5
Something which I really appreciate about these value witness types is that you can implement some really interesting higher-order functions on them. For instance, a "pullback" function, where when you give it a function from (B) -> A
, it gives you back a value witness type over B
:
extension MyCustomString {
func pullback<B>(_ f: @escaping (B) -> A) -> MyCustomString<B> {
return MyCustomString<B> {
self.inString(f($0))
}
}
}
struct Account {
var transactionAmounts: [Double]
var name: String
}
let anonymousAccountString: MyCustomString<Account> = MyCustomString
.collection(of: .double)
.pullback(\.transactionAmounts)
let someAccount = Account(transactionAmounts: [19.99, 21.45, 5.99, 1.99, 50.00], name: "Johnny Appleseed")
print(anonymousAccountString.inString(someAccount))
// 19.99, 21.45, 5.99, 1.99, 50.00
And we can even improve the ergonomics by using callAsFunction(_:)
:
extension MyCustomString {
func callAsFunction(_ value: A) -> String {
return self.inString(value)
}
}
print(anonymousAccountString(someAccount))
// 19.99, 21.45, 5.99, 1.99, 50.00
If you're interested, @mbrandonw did a great talk about it: Protocol Witnesses - Brandon Williams - App Builders 2019.