Partially applied generics

let's say I have two values:

let a: Something<A, X>

and

let b: Something<B, X>

then I would like to write a third function that cares about the X:

func example(param: [Something<_, X>]) {
   // ...
}

example(param: [a, b])

1 Like

At best, it'd be sugar for:

func example<NotCare>(param: [Something<NotCare, X>]) {
   // ...
}
1 Like

@Lantua, Is that true? Your NotCare is a specific type, and all the params have to have the same NotCare type, no? My idea is that we can mix the types that we don't care about.

You want heterogeneous generics that share a common type, I see. I'm not sure how the compiler can automate the type erasure required to make that happen.

Not sure. Complete noob idea, but perhaps It can generate some makeshift abstract supertype..

You'd need to box it for sure. What I'm not sure is how the synthesized box would look like. And you wouldn't be able to use any function that specify the first parameter (let's call it First).

It starts to look a lot like Protocol with Associated Types (PATs), which has its fair share of limitations.

Hm, here's something that seems to work already:

protocol MakeshiftAbstract { } 
extension A: MakeshiftAbstract { } 
extension B: MakeshiftAbstract { } 

and then example would be:

func example(param: [Something<MakeshiftAbstract, X>]) {
   // ...
}

of course we'd need to upcast a and b to Something<MakeshiftAbstract, X>.

So it just seems doable to me. The compiler would just need to look at all the types that are _'d and generate an empty protocol and make those type apply, then substitute automatically in the callsite of example.

You can just use Any for that.

The problem arises quickly when Something wants a more complex First, like Dictionary.Key. You can't just synthesize a MakeShiftAbstract that is Hashable. AnyHashable implementation is complex, and part of it isn't even expressible in Swift. I wouldn't expect any automated boxing to do what you want.

And if you just want to use for homogeneous protocol (Protocol without Associated Types), you can just use Any + protocol as needed.

2 Likes

Agreed, but in this case the compiler can skip that implementation, because omitting it is kind of the point here. It won't be used wen it's _'d.

And Any isn't limiting enough. I want some types, as defined by the code that I type.

I'm not sure I follow.

Could you elaborate more on that?

Which case are you referring to? You mean the Dictionary.Key case, or Something.First case?

I just mean that the compiler, when using this approach, would not need to synthesize a protocol that is valid just like any protocol written by the user would have to be. It has the luxury to bend the rules to make the type disappearance happen.

These are just naive assumptions of course.

As I'm aware, Swift doesn't allow automatic type casting of generic types via type parameters' casting.

struct Box<Value> {
   let value: Value
}

let b: Box<Int> = Box(value: 3)
b is Box<Any> // false, even though 3 is Any

It sees Box<Int> and Box<Any> as unrelated types. If it allowed this kind of implicit casting, then your example would just work using Any instead of _:

func example(param: [Something<Any, X>]) { ... }