SE-0309: Unlock existential types for all protocols

I think both any P and _ & P are not sufficient. They are useful as a convenience shortcut for common cases, but not sufficient to cover all the cases.

Consider the example from the proposal:

extension Sequence {
  public func enumerated() -> EnumeratedSequence<Self> {
    return EnumeratedSequence(_base: self)
  }
}

let s: any Sequence = [1, 2, 3]
let e = s.enumerated()

Even after SE-0309 this does not type check, but in theory it is possible to lift restrictions further. Then to express the type of e we would need something like any<T> EnumeratedSequence<T> where T: Sequence. For this specific example EnumeratedSequence<_ & Sequence> would work too, but in general case T may occur inside the type multiple times.

For example:

protocol P {}
extension Int: P {}
extension String: P {}
struct KeyWrapper<T: P>: Hashable {}
struct ValueWrapper<T: P> {}

let a: Dictionary<KeyWrapper<Int>, ValueWrapper<Int>> = ...
let b: Dictionary<KeyWrapper<String>, ValueWrapper<String>> = ...
let x: Array<any<T> Dictionary<KeyWrapper<T>, ValueWrapper<T>> where T: P> = [a, b]

Alternatively, this can be expressed in a top-down fashion using constraints if we can refer to generic parameters as associated types:

...
let x: [any Dictionary where Key == some KeyWrapper, Value == some ValueWrapper, Key.Wrapped == Value.Wrapped, Key.Wrapped: P] = [a, b]

See Syntax for existential type as a box for generic type for more.

This will become even more import once Swift gets unboxing existential. Unboxing gives a generic type of local scope, and to escape this scope, any value needs to either have a non-generic type, or be wrapped into an existential container.

1 Like