Fixing Angle Bracket Blindness (and unboxing existentials while we're at it...)

It's possible. You’re correct to point out that one could not refer to the opened type of lhs by name. At first glance this may seem like a big deal, but in fact it’s not an issue:

To preserve the signature verbatim, you'd use two functions:

/* static can't be declared outside a type */
func == (lhs: any Equatable, rhs: Any) -> Bool {
  _equals(lhs as some Equatable, Any) // (1)
}

func _equals<T: Equatable>(lhs: T, rhs: Any) -> Bool {
  if let rhs = rhs as? T {
    return lhs == rhs
  } else {
    return false // Probably not wise, but we're not here to discuss that.
  }
}

Now, you'll notice that Doug has proposed implicit existential opening, so in fact any arguments that can be passed to == can be passed to _equals without explicitly opening the existential first (shown at 1), making the entirety of the outer wrapper function redundant. In other words, if you're not under ABI stability constraints, you don't need to go through this trampoline at all and can just write:

func == <T: Equatable>(lhs: T, rhs: Any) -> Bool {
  if let rhs = rhs as? T {
    return lhs == rhs
  } else {
    return false // Still probably not wise, but we'll move on.
  }
}

Notice that not only is this very straightforward to write, it's a function that you can write already; Doug's proposed solution would make it Just Work to pass an existential box as the first argument. So in fact the genius of Doug's proposed solution is that it makes what you're asking for not just possible but also simultaneously unnecessary. Nice.

2 Likes