I have a templated struct in a Swift library I am writing. This struct has two characteristics:
Foo<T> wraps a TfooA: Foo<A> and fooB: Foo<B>, then fooA + fooB should be of type Foo<(A, B)>.This works well enough when there are only two types to combine, but when you chain this combination operation you start getting nested tuples, which is not what I want. For example, in the following code:
let a = Foo<A>(/* initialize */)
let b = Foo<B>(/* initialize */)
let c = Foo<C>(/* initialize */)
let d = a + b // type is Foo<(A, B)>
let e = d + c // type is Foo<((A, B), C)>
d has type Foo<(A, B)>, which is what we want, but e has type Foo<((A, B), C)>, which is an extra level of unwanted nesting.
I need some way to express that the combination of a Foo<A> and a Foo<B> is not a Foo<(A, B)>, but rather a Foo<A + B>, where + is a hypothetical static operation which means "if the first type is a tuple type, append the second type onto it, yielding a new, non-nested tuple type. If it is not a tuple type, simply make the tuple type (A, B).
This feels like pushing to compiler to (beyond?) its limits, and I suspect that it might not be possible with Swift's current templating capabilities and type system. Still, if anyone can offer a workaround, a redesign that doesn't encounter this problem in the first place, or something conceptually similar but not identical to what I'm trying to do, it would be extremely helpful. As things stand, I'm at an impasse.
I think there is no generic way to do that.
Maybe, you can do something like this:
struct Foo<T> {
let v: T
init(_ v:T) { self.v = v }
}
func +<A,B>(lhs: Foo<(A)>, rhs:Foo<(B)>) -> Foo<(A,B)> { return Foo(lhs.v.0, rhs.v.0) }
func +<A,B,C>(lhs: Foo<(A,B)>, rhs: Foo<(C)>) -> Foo<(A,B,C)> { return Foo(lhs.v.0, lhs.v.1, rhs.v.0) }
func +<A,B,C>(lhs: Foo<(A)>, rhs: Foo<(B,C)>) -> Foo<(A,B,C)> { return Foo(lhs.v.0, rhs.v.0, rhs.v.1) }
func +<A,B,C,D>(lhs: Foo<(A,B,C)>, rhs: Foo<(D)>) -> Foo<(A,B,C,D)> { return Foo(lhs.v.0, lhs.v.1, lhs.v.2, rhs.v.0) }
func +<A,B,C,D>(lhs: Foo<(A,B)>, rhs: Foo<(C,D)>) -> Foo<(A,B,C,D)> { return Foo(lhs.v.0, lhs.v.1, rhs.v.0, rhs.v.1) }
func +<A,B,C,D>(lhs: Foo<(A)>, rhs: Foo<(B,C,D)>) -> Foo<(A,B,C,D)> { return Foo(lhs.v.0, rhs.v.0, rhs.v.1, rhs.v.2) }
// ... as many as you want ...
let f1 = Foo<(Int, UInt)>(1, 2) + Foo<String>("string") // -> as Foo<(Int, UInt, String)>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With