I was delighted to discover a certain behavior of code-completion which I'll detail below. However, I tried to push it one step further and found that code-completion stopped working - I'll also detail this step that breaks the camel's back. What I'd like to know is the likelihood that this will work in the future, and of course if a specific timeline is known that would be great.
Here are two essentially trivial enums:
enum AIdentifier {
case a
}
enum BIdentifier {
case b
}
They are so simple because their only purpose will be to serve as distinct types to allow for easy switching between function overloads.
Here are the corresponding types which our enums are serving as identifiers for:
struct A {
static func foo (arg: Bool) -> A {
A()
}
static func bar (arg: Int) -> A {
A()
}
}
struct B {
static var baz: B {
B()
}
}
The last piece of the setup is the previously-alluded-to overloaded function:
func C (_ identifier: AIdentifier, _ a: ()->A) { }
func C (_ identifier: BIdentifier, _ b: ()->B) { }
The thing that I was initially delighted to find is that, given this setup, if I type:
C(.)
then the code-completion menu offers both .a and .b. As soon as I pick one of the two identifiers, the proper overload is now known which means that the type of the second parameter is known, which allows for even more code-completion magic. If I type . inside the braces of the closure:
C(.a) {
.
}
then code-completion very nicely shows me my two options, which are .foo(arg:) and .bar(arg:):
C(.a) {
.foo(arg: true)
}
So far so good. The discoverability that this allows for is absolutely amazing. What I really want though is for .foo(arg: true) to bring with it an associated type which can propagate onto further usage. I want .foo(arg:) to have a different associated type than .bar(arg:). Here are the associated types for each method:
struct X { }
struct Y { }
I'll re-define A to be generic, and change .foo(arg:) and .bar(arg:) to return values specialized with different generic parameters:
struct A <T> {
static func foo (arg: Bool) -> A<X> {
A<X>()
}
static func bar (arg: Int) -> A<Y> {
A<Y>()
}
}
Now that A<T> is generic I have to adjust the overload of C(_:_:) that is associated with it. I'll also add a return type which will better illustrate the use case that I'm pursuing here:
func C <T> (_ identifier: AIdentifier, _ a: ()->A<T>) -> D<T> { }
where D is defined as:
struct D <T> {
func sink (_ handler: @escaping (T)->()) { }
}
The first step of code-completion still works with this setup - if I type C(.) I am still offered my two choices, .a and .b. However, after choosing .a and moving on to the value inside the closure, code-completion breaks down. Now that the first overload of C(_:_:) is generic, the two static functions on the A type are no longer offered by code-completion, despite the fact that they satisfy the type constraints of the context. This code still compiles, but has to be typed out by memory because it is not offered by code-completion:
C(.a) {
.foo(bar: true)
}
.sink { value in
print(value)
}
The advantage of this code is of course that the value argument in the sink closure is known to be of type X, and that if I simply change .foo(arg: true) to .bar(arg: 2) then suddenly the value argument is of type Y. That's pretty huge for me. However, I think that the code-completion offered by the less complex type inference demands currently outweighs the value of the type-safety for me. The ideal would of course be to have the best of both worlds. Does anyone have any thoughts on this? I'm open to estimations of timeline, conjectures as to whether this can or will ever be possible, or alternative architectural solutions. Thanks!