Reverse generics for argument type?

One of the key motivations for opaque types in return position was that library authors could hide the internal implementation details and instead only provide a constraint, so that library could evolve internally without breaking the API. I wonder if similar possible use case could exist for the function parameter position as well. Although maybe those cases can already be solved with some other way, like just using regular generics...

1 Like

Iā€™m pretty sure the compiler can already specialize the existential-taking function. (Hopefully someone with more expertise in the compiler can say whether bar is guaranteed to be optimized equally well as foo.)

The real difference is that with generics you can tell the compiler about same-type constraints:

func foo<T: Protocol>(_ p: T, _ q: T) {
  // p and q are known to be the same type
}

func bar(_ p: Protocol, _ q: Protocol) {
  // p and q could be of different types
}
3 Likes

You refer to the example with two Self parameters, the only constraint the callee (i.e. ==) adds to the context is that both parameters must be the same which is currently only unique for the same variable or different aliases to the same variable.

But == doesn't specify what Self has to be for a type, that is specified by the caller, in our case it was specified by another callee with name Private which was executed in the caller.

First I wouldn't call a value an existential, this is more kinded to a variable.
Second, we could also store the proofs of some P inside the value of some P and unwrap the existential behind, but why we should do so if there is the option to optimize the boxing of some P out by simply replacing the box with the underlying type.

Yes, I see your point.
The Swift definition of existentials is more related to implementation details, my is more of abstract nature. So you are mostly correct with your conclusion.

Is it really identical? Current behavior is not totally the same. Is the conformance also the feature that would be achieved in the future Swift? (I know it's true for Error)

// Non-Existential Type (concrete type)
let value1: Struct= Struct()
// Existential Type (protocol as type)
let value2: Protocol = Struct()

let _ = foo(value1)  // possible
let _ = foo(value2)  // impossible, protocol 'Protocol' as a type cannot conform to the protocol itself
let _ = bar(value1)  // possible
let _ = bar(value2)  // possible

In some cases, this may not be possible, and a distinction must be made.

protocol Protocol {
    static func bar() -> Int
}

struct Struct: Protocol {
    static func bar() -> Int {
        42
    }
}

func foo<T: Protocol>(_ value: T) {
    let value = T.bar()
}

// What happens if call `foo` with protocol type
// `Protocol` doesn't have any implementation
let value: Protocol = Struct()
foo(value)
1 Like