Hello, I couldn't quite follow some of the arguments regarding this topic. I wasn't sure if I should interfere, but I hope it will be helpful now that I did. :/

But the flip side of that is that if they choose not to use X, Y would just sit there, unused. Best case scenario, it would just increase their image size; worst-case scenario is that it could preclude some future optimisations that may require a full program analysis.

Not sure about the worst-case. The current implementation already checks during initialization whether the service is usable. After all, it throws an exception when it is not. Any optimization can just ignore the service provider in such case. There is no full program analysis required, which is not already part of every module initialization.

To do this properly, as Alan alluded, `requires static` is insufficient because the module doesn’t know where the service interface is supposed to come from. Rather, we would need a new specific mechanism that says “if module X is readable, then provide this service”.

Why do you need the information which module provides the service interface? I currently don't see how this information would be helpful. I could also imagine cases where you want to provide an optional service without using `requires static`. For example, imagine a framework introduces a new service interface. You may want to provide an implementation for the service interface while keeping support for previous versions of the framework.

The module system then sees that Y wants to provide a service, but the module system has no idea where the service is, so fails fast at startup. This is a feature, not a bug. The classpath behavior, of only discovering that a service provider can't provide a service during run time (because the service is missing), is not what we wanted for modular services.

Does this issue really exist? The module system would already discover that during initialization. When the service interface is not available, the service provider will be ignored, there is no error (or anything else) later during runtime. Besides that, if the service interface is not available, you are unable to invoke the service loader anyway. Invoking the service loader requires a class instance of the service interface, which is not available. If you would still try to invoke the service loader, you would get a NCDFE at the call site of the service loader, the provides clause wouldn't make a difference.

I am not quite sure about the behavior if you stack multiple module layers on top of each other. I think the service loader should only load instances that implement the class from the same module layer as the class instance given to the service loader. Everything else would cause a runtime error independent of the issue we are currently discussing. In this case, ignoring the provides clause would be the right solution, even if X would be available on another module layer.

I currently see only two reasons for keeping the current behavior. The first one is to discourage service providers to be casually added to modules. You could argue that service providers should have their own module, so that users can add and remove them individually. The second reason might be the desire to fail early when the service interface was renamed or removed after updating X. This could be addressed by adding a modifier like `provides static`, there is no need to correlate the provides cluase with a specific `requires` clause.

So I think a more general solution than adding a way to describe “this service is conditional on the presence of X” would be to allow multiple modules in a single JAR

I guess that would work as well, but it would probably be more complex to set up.

Best regards
Johannes

Reply via email to