Proposal: #CompileTimeDependences: `requires static`

mark.reinhold at mark.reinhold at
Thu Jun 30 23:07:58 UTC 2016

2016/6/29 2:30:28 -0700, tim_ellison at
> mark.reinhold at wrote on 28/06/2016 22:17:15:
>> Issue summary
>> -------------
>>  #CompileTimeDependences --- Provide a means to specify a module
>>  dependence that is mandatory at compile time but optional at run time,
>>  for use with libraries that are not strictly necessary but can be
>>  leveraged if present at run time.  (Detail: If a dependence is
>>  "optional at run time" then does the module system try to satisfy it
>>  during resolution but fail silently if it cannot, or does it ignore it
>>  during resolution but add the corresponding read edge to that module if
>>  it was resolved for some other reason?) [1]
>> Use cases for this feature include annotations that need not be present
>> at run time [2] and "auto-configuring" libraries and frameworks such as
>> Joda Beans [3] and Spring [4].
> For some reason I can't quite explain, this feels like we are giving up
> on one of the fundamental completeness assurances of a coherent modular
> application.  Is this a new failure mode for link/run time modular systems
> that has not previously existed?

This proposal reflects a fairly common existing practice, at least in
Java.  Today it's most often expressed via optional dependencies in
Maven [1] and carefully-written code that either uses reflection or is
prepared to catch the appropriate exceptions in case an optional
component is not available at run time.  I wouldn't characterize this
practice as a failure mode but, rather, a flexible way to configure
software systems.

> It may be that this proposal satisfies an important compatibility story for
> existing code bases, and that is the justification, because on first reading
> my reaction is that the code has to be written to deal with the absence of
> the optional module anyway, more details below.

Yes, that's correct, but see below.

>> Proposal
>> --------
>> Extend the language of module declarations to allow the `static` modifier
>> to be used on a `requires` directive, with the following meanings:
>>  - At compile time, `requires static M` expresses a mandatory
>>    dependence.  It is an error if a suitable module cannot be found
>>    amongst the observable modules and resolved.
>>  - In phases after compile time, `requires static M` expresses an
>>    optional dependence.  The module system will not search the
>>    observable modules for a suitable module during resolution, but
>>    if the resulting module graph contains a suitable module then it
>>    will add the appropriate readability edge prior to doing the usual
>>    post-resolution sanity checks.  (The parenthetical question in the
>>    issue summary is, in other words, answered in the negative.)
> I read the parenthetical question as an either-or ...

Quite right -- I should've written "answered by taking the second

>> ...
>> The code in `joda.beans` that refers to types in `joda.collect` must, of
>> course, be written defensively so that it fails gracefully at run time
>> when the `joda.collect` module is not present.
> This is a key point.  Since the application code has to be robust in the face
> of a missing "requires static" module, wouldn't it be better served by coding
> the behvior of the required module as a service that has a "fail gracefully"
> implementation and can be replaced by a productive implementation if required
> at link/run time?

Some uses of compile-time dependences could be better expressed as
services, but that's not always the case.  If you're trying to leverage
a large third-party library (e.g., Guava) when it's present at run time,
but can live without it when it's not, then doing that with services
would require you to abstract the Guava functionality that you want to
use into one or more service types, which is apt to be awkward at best.
The situation gets even worse with large auto-configuring frameworks
such as Spring [2].

>> ...
>> Notes
>> -----
>>  - In phases after compile time, why doesn't the module system search
>>    the observable modules in an attempt to satisfy an optional
>>    dependence?
>> This would not be difficult to arrange but it could lead to surprising
>> behavior in which optional dependences silently cause many additional
>> modules to be resolved.  Given the use cases we have it does not seem
>> unreasonable to expect an end user to indicate explicitly, either in
>> source code or out-of-band (e.g., with a command-line option such as
>> `-addmods`), that an otherwise-optional module is actually needed.
> Another option would be to provide a command-line option that explicitly allows
> the linker/launcher to ignore missing modules that are required where there is
> an assurance that it will not actually be required in the particular context;
> that is, if I design a single module with "requires" or "requires static"
> dependencies if it is used in a stand-alone or container context, for example.
> I'd prefer to fail because of an explicit -ignoremods option on the command
> line rather than a missing -addmods.

The existing common practice with JAR files is to trust end users to
place desired optional JAR files on their run-time class paths.  The
current proposal reflects that practice, by asking end users to add
desired optional modules via -addmods.

We could reverse the sense of all this, change the resolver to try to
satisfy optional dependences at link time and run time, and tell people
to use -ignoremods when they specifically don't want an optional
dependence to be resolved, but I think that would be significantly
harder to explain and to understand, and hence a burden to migration.

- Mark


More information about the jpms-spec-observers mailing list