Method Dispatch for Methods in Protocol Extensions in Separate Modules

Hello,

Project Layout

Let there be a Swift executable project with the following layout (Frizlab’s Demo Projects / swift-test-modules-and-protocol-extensions · GitLab):

  • The Protocol target which defines an empty protocol named Protocol;
  • The Extension target which depends on Protocol and defines an extension to Protocol with a function hello() and a default implementation for the function (we have to define an implementation because we’re in a protocol extension);
  • The main target which depends on both previous targets and creates a concrete implementation (named Implementation) of Protocol which implements the method defined in the extension.

My problem

When is the default implementation of the protocol called vs. the concrete implementation? Also, is the current behaviour the expected behaviour?

Tests

In the main module, let there be a variable implem which is an instance of Implementation.

The concrete non-default implementation of the hello() method is only called in two cases:

  • Calling the hello() method directly on the object;
  • Calling the hello() method in a function that takes an Implementation argument.

These are the other tests I did that call the default implementation of the method:

  • Calling the hello() method in the main module in a function that takes a Protocol argument;
  • Calling the hello() method in the main module in a generic function that takes a P argument where P: Protocol;
  • Same cases with the methods in the Extension module.

My Thoughts

I understand the method dispatch in Swift is not as dynamic as Objective-C. However, for the generic case at least, I’d have expected it to be smart enough to dispatch the hello() call to the concrete implementation instead of the default one.

I think in their current form, protocol extensions are pretty much useless when defined in an external module. Whenever a method from the extension is called in said module, there is no way the actual concrete implementation of a method defined in the extension is called. Does anybody has any insights on this?

Thank you very much.

Modules have nothing to do with this.

Protocol requirements are customization points, and they are dynamically dispatched.

Extension methods which are *not* protocol requirements, are not customization points.

2 Likes

Oh. It appears you’re right :frowning:

I tested with a protocol and extensions in the same module and when calling from a method which takes the protocol as parameter, the default implementation is called.

Which begs the question: is it possible to extend protocols with a dynamic dispatch? Otherwise protocol extensions would not be very useful, would they?

If the extension is visible from the place you declare conformance to a protocol, then its methods are available as candidates to be chosen to satisfy a protocol requirement. The mapping of protocol requirements to implementations is fixed from the context the protocol conformance is declared, though, and can't be dynamically modified once established. This is to prevent the problems with ObjC categories clashing and unexpectedly changing the behavior of other unrelated code at runtime.

It would be a useful future extension for extensions to be able to add new dynamically-dispatched requirements to protocols, but currently extensions only add methods to types that conform to the protocol, not to the protocol itself.

2 Likes

Alright, thank you very much for your explanations. I hope we’ll see dynamically-dispatched requirements in protocol extensions soon! In the mean time I’ll work around it.

It would be a useful future extension for extensions to be able to add new dynamically-dispatched requirements to protocols, but currently extensions only add methods to types that conform to the protocol, not to the protocol itself.

Do we have this feature now in 2024?

There’s no way to express this directly, even six years later in 2024 (and it would be a major departure from how the language works). One thing you can try if you haven’t already is to define a generic type as a sort of adapter, like so, where Q is a protocol with the “new requirements” extending P:

struct G<T: P> : Q {
  var wrapped: T

  // implement requirements of Q in terms of P on wrapped 
}
1 Like

@Slava_Pestov My use case is different (posted here - and that's where I got referred to this thread).

I want to break up my SceneDelegate into different targets. Problem is, those methods in the other target are not seen as part of the SceneDelegate class. Thanks to this thread, I understand the problem with Swift extensions, but ObjC categories should work? I attempted the same using ObjC category i.e., I created the SceneDelegate in the ObjC layer in a static library and extended it another library. The delegate methods defined in another library are not invoked by iOS.