Is it possible to automate the making of type erasure?

I'm talking about things like AnyHashable, AnyView, etc.

Ideally we shouldn't need to hand code type erase because they all seem to be very boilerplate. Can the compiler just do it by itself by making the compiler more smart?

1 Like

Yes. What you're talking about is called an "existential" (things like Any and AnyObject). You can do that with regular protocol types, without needing to box them in an AnyMyProtocol type:

protocol MyProtocol {
  func doSomething() -> Int
}

func myFunction(_ p: MyProtocol) {
  let x = p.doSomething()
}

var existential: MyProtocol = //...

Unfortunately, that doesn't currently work with protocols with associated types ("PAT" for short) or that use Self. Hashable, Collection, View and certain other common protocols have pre-built boxing types which emulate existentials, and as you're aware, you can also build your own. It's a known issue that will hopefully be alleviated one day by enhancing the compiler.

If you want to know more, "generalized existentials" or "PATs" are search terms you might find helpful.

1 Like

Wait, why is there an AnyHashable type? Hashable doesn’t (currently?) have Self/PAT requirements, right?

Hashable conforms to Equatable, which does have a Self requirement

4 Likes

Also, as I recently discovered, it is not possible to implement AnyHashable (or AnyEquatable) in pure Swift.

If a class Super implements Equatable, and then it has two different sub-classes Sub1 and Sub2, then there is no way to implement the == operator for AnyEquatable correctly in pure Swift.

Instances s1: Sub1 and s2: Sub2 which compare equal via Super.== still need to compare equal as AnyEquatable(s1) and AnyEquatable(s2), but neither Sub1 nor Sub2 can be converted to each other, and there’s no way to find their common ancestor class which conforms to Equatable and implements ==.

The standard library type AnyHashable is written partially in C++ specifically so that it can find that common ancestor implementing Equatable.

7 Likes

Is Unlock Existential Types for All Protocols "generalized existentials"?

I guess you could technically call it "generalized existentials", as it allows any protocol and protocol composition type to offer an existential type. The generics manifesto specifically says:

"The restrictions on existential types came from an implementation limitation, but it is reasonable to allow a value of protocol type even when the protocol has Self constraints or associated types"

However, you should keep in mind that the proposal won't enable use of requirements that use associated types. For instance, this will not work:

protocol Identifiable {
  associated type ID : Hashable
 
  var id: ID { get }
}

let identifiable: Identifiable = ...

identifiable.id ❌
// The requirement 'id' uses the associated type
// 'ID' which is prohibited; therefore we get an error.

From my findings, the only thing holding this back is Unlock Existential Types for All Protocols. There's simply no way to tell the compiler that some arbitrary superclass type conforms to Equatable without being able to do something like as! Equatable or if let type = superclassType as? Equatable.Type. Once we have that, being able to write AnyHashable or AnyEquatable in pure Swift will be possible, albeit with a little reflection help.