I'm assuming there is no support in the language for what I want at the moment but maybe you know something I might be missing.
There is a module with these definitions:
public protocol P {
associatedtype T // P is PAT
}
public protocol SomeDelegate {
func foo<T>(p: T) where T: P
}
On my side I'm defining my own protocol that conforms to P and want to use its requirements:
protocol P1: P {
var bar: Baz { get }
}
class SomeDelegateAdopter: SomeDelegate {
func foo<T>(p: T) where T: P {
// How can I access `bar` if `p` is `P1`?
}
}
At least something like this naive method would be nice since instance of PAT cannot be returned as an existential:
func p1<T, U>(from value: T) -> U? where T: P, U: P1, T == U {
value as? U
}
I would very like to avoid using a "shadow protocol" for purpose of the upcasting.
That's what would be ok for me but unfortunately I don't think function overloading works like this when protocol requirement is involved. I gave it a try but func foo<T: P1>(p: T) is never called.
There is, technically, a way to do this, but it’s kind of a doozy.
The second half of my post here has some links explaining how. The simplest approach is the one by Dave Abrahams.
The basic idea is to make a marker protocol with requirements that mirror the PAT, except instead of using associated types the requirements are generic. Then you make a placeholder generic type that conditionally conforms to the marker protocol when its generic argument conforms to the real protocol.
Notably, Dave’s approach uses a single generic type, with a helper method to do the necessary casting in one place. Then it can conditionally conform to as many marker protocols as you want.
Thanks @Nevin! It is cumbersome but indeed best what I've seen. I'm again amazed what mind-blowing workaround needs be done in order to get essential semantics from these protocols. For anyone interested in the implementation I reduced Dave's approach to this:
struct PDispatch<PAdopter> {
func toAdopter<T>(_ x: T) -> PAdopter where T: P {
x as! PAdopter
}
}
protocol P1Dispatch {
func bar<T>(_: T) -> Baz where T: P
}
extension PDispatch: P1Dispatch where PAdopter: P1 {
func bar<T>(_ x: T) -> Baz where T: P {
toAdopter(x).bar
}
}
extension P {
var p1Dispatch: P1Dispatch? {
PDispatch<Self>() as? P1Dispatch
}
var p1Bar: Baz? {
p1Dispatch?.bar(self)
}
}
Now it can be used like this:
class SomeDelegateAdopter: SomeDelegate {
func foo<T>(p: T) where T: P {
if let bar = p.p1Bar {
// :-)
}
}
}