An example of what the implications would be of splitting the provider into two separate interfaces.
A number of the classes involved here are tests, so the change is not, necessarily, quite as large as it looks. However, it is still extremely large.
I count 6 new classes and interfaces in src alone, all of which are 95% duplication.
There's 4 new test classes. While not needed at runtime, it does show that making sure that both provider pipelines continue to work is, well, double the effort.
Most of the duplication comes from one of 2 places:
The interface is different, thus the type hint has to be different.
The notification version has fewer parameters, because it doesn't have to consider ordering. However, the presence of ordering has no affect on the notification version. That is, even if users are able to control the order, that has no importance on the spec, by design. That is, there's no actual benefit over using the same provider and just ignoring those parameters.
** It may be possible to simplify a bit further by factoring some code out to base classes or traits, but both of those feel like code smells.
The purported benefits of splitting the provider interface are:
1) We don't have to come up with a new name for "Event" if we rename Task to Event.
2) Users can have more targeted providers for each pipeline, since no listener will ever apply to both object types, which can be more performant.
3) It's confusing to have a single provider for 2 different things.
For points 2 and 3, that is trivially resolved by simply instantiating the provider twice. There is absolutely nothing in the spec (when the provider is not split) that forces an implementer to use the same provider object for both cases. That solves the performance question completely without any changes.
Nor is there anything that requires an implementer to have only one implementation; with a single interface it is still absolutely spec-legal to have two separate provider implementations, with different internal capabilities. Or they can use the same provider implementation. That's entirely up to the implementer to work out as they find most useful. By forcing it to be 2 separate interfaces, however, it becomes impossible to combine any part of the supporting code in a meaningful way.
(This can also be further clarified in the spec or meta-document.)
In fact, it would be super simple to implement a BasicProvider object that is essentially the RegisterableNotificationListenerProvider shown here and offer it as an alternative. Users/implementers could use either one in either case. (Say, if they want Tasks but don't care about order.) But by using the same interface, 2/3 of the code duplication shown here goes away.
In short, I see many, many downsides to forcing two separate interfaces and only marginal benefit: That we can rename Task to Event without having to come up with a new name for the generic case. I find that extremely uncompelling. At this point in the process, if we want to s/Task/Event/ I would say the onus is on those arguing to do so to come up with an alternative name. :smile: The net effect of avoiding it is substantially negative.
An example of what the implications would be of splitting the provider into two separate interfaces.
A number of the classes involved here are tests, so the change is not, necessarily, quite as large as it looks. However, it is still extremely large.
The purported benefits of splitting the provider interface are: 1) We don't have to come up with a new name for "Event" if we rename Task to Event. 2) Users can have more targeted providers for each pipeline, since no listener will ever apply to both object types, which can be more performant. 3) It's confusing to have a single provider for 2 different things.
For points 2 and 3, that is trivially resolved by simply instantiating the provider twice. There is absolutely nothing in the spec (when the provider is not split) that forces an implementer to use the same provider object for both cases. That solves the performance question completely without any changes.
Nor is there anything that requires an implementer to have only one implementation; with a single interface it is still absolutely spec-legal to have two separate provider implementations, with different internal capabilities. Or they can use the same provider implementation. That's entirely up to the implementer to work out as they find most useful. By forcing it to be 2 separate interfaces, however, it becomes impossible to combine any part of the supporting code in a meaningful way.
(This can also be further clarified in the spec or meta-document.)
In fact, it would be super simple to implement a
BasicProvider
object that is essentially theRegisterableNotificationListenerProvider
shown here and offer it as an alternative. Users/implementers could use either one in either case. (Say, if they want Tasks but don't care about order.) But by using the same interface, 2/3 of the code duplication shown here goes away.In short, I see many, many downsides to forcing two separate interfaces and only marginal benefit: That we can rename Task to Event without having to come up with a new name for the generic case. I find that extremely uncompelling. At this point in the process, if we want to s/Task/Event/ I would say the onus is on those arguing to do so to come up with an alternative name. :smile: The net effect of avoiding it is substantially negative.