An Implementation Model for Rational Protocol Conformance Behavior

With that thought in mind, I've been taking notes, and trying to harmonize, correlate and synthesize. As part of that process, I've been applying what has been discussed in this thread to past examples of difficult-to-reason-about conformance and dispatch behavior.

On the theme of choosing more-specialized witnesses at witness-table instantiation time, one of the examples I've been trying to reason about it is set forth, below.

protocol P {
  func a() -> String
  func b() -> String
}
extension P {
  func a() -> String { "slow a()" }
  func b() -> String { "doing general stuff with \(self.a())" }
}
protocol Q: P, Equatable {}
extension Q {
  func a() -> String { "fast a()" }
  func b() -> String {
    let x = self.a() // Since self must conform to Q, shouldn't
      // self.a() always dispatch either to Q.a() or to a 
      // customization of a() on the concrete type?
    return "doing special stuff with \(x)"
  }
}

extension Int: Q {}
print(Int(1).b()) // As expected, does special stuff with fast a().

struct R<T>: P { let value: T }
extension R: Equatable where T: Equatable {}
extension R: Q where T: Equatable {}
print(R(value: 1).b()) // Does SPECIAL stuff, but with SLOW a(). Is that right?

If I'm understanding the intended model correctly, in the context in which self.a() is called, self is known to always conform to Q. Since Q is the most general possible form of self, the witness table entry for conformance to a() should point to Q.a(). Am I misunderstanding how the intended model works?

[EDIT: For clarity, I removed the where Self: Equatable conditional conformance on protocol Q, and replaced it with the more direct declaration that Q inherits from Equatable. Also, removed the unnecessary implementation of ==, in favor of automatic synthesis.]

3 Likes