Suggestions for improvements of Java modules
stef at epardaud.fr
Thu Dec 17 13:51:44 UTC 2015
As part of the integration with Ceylon we observed the following
limitations of Java 9 which I'd like to ask you to consider:
= Optional module imports.
This has been pointed out by various users and it should be simple to
implement because they would be required at compile-time and optional at
run-time, where the linker would just not link that module if it can't
find it in the module path.
That's pretty useful in many module systems to describe dependencies
that will be used if present, but not in the minimal runtime deployment.
Discovery is typically via reflection of a class of the optional module,
but could be extended by an API of the module such as
`this.getClass().getModule().getLayer().hasModule("com.foo")` (or similar).
= Versions for module imports.
I know this is meant to be off-topic and left to build tools, and I
understand why Java wants to leave that hard problem to others.
But in much the same way that Java 9 modules support setting an optional
module version in the jar tool, ending up as an optional module
descriptor class attribute, it would be really great if the module
descriptor class file allowed for optional module import versions.
Even if the source `module-info.java` does not require them, even if it
does not allow them optionally. If the binary form allows versions to be
put in there, which would represent the version used at compile-time
(even javac could fill it in), then that information is going to be very
useful because it means we can take a compiled Java 9 module and we know
what it was compiled against. It means that I can run that module
without having to download other descriptors.
Of course I would suggest that it be possible to specify them in the
source module descriptor and javac be able to check that the modules it
has in its module path correspond to what the user wants to depend on,
but again, if you don't want that, its optional presence in the binary
form would already be much better, so that other languages can store
(and use) that info.
= Module cycles.
This is expressely disallowed at compile-time and allowed at run-time
only via reflection.
I found that to be very annoying. I understand the arguments against
module cycles boil down to suggesting to merge them as a single module.
This is in effect what forces `java.base` to include collections, time
and lots of other things. I understand that argument, and I agree that
importing a module with cycles is equivalent to importing the whole group.
But it is very useful in terms of authoring to split up a large module
into inter-dependent ones. Each module can have its own maintainer and
test suite, and they often do.
Not only that, but there's no good reason to not allow cycles. The javac
compiler can deal with them trivially as long as it can compile both
modules at once. Exactly like it deals with Class cycles. And we already
know that the run-time allows it via reflection. Just not the linker,
for some reason. Semantically, if we allow inter-dependent classes, and
packages, I don't understand why we disallow inter-dependent modules.
And last, it would be much better for cases where there's only a
run-time cycle. For example I have a module A which defines some
interfaces and uses (depends on) a module B which implements a
plugin-loading system which uses those interfaces via reflection. Module
B does not depend on Module A at compile-time, but at run-time it uses
reflection to read Module A annotations and so it will depend on it. At
the moment I have to add a silly static block _somewhere_ in Module B
(hopefully at the Right Place™) to add the dependency (module read) via
the Module reflection API, so that Java reflection does not throw me out.
Frankly, I'd much prefer to express this cycle dependency somwhere where
it could be added during linking because at least I'd be sure it's added
at the right time and I don't risk taking a code-path that bypasses my
run-time cycle addition and get to a reflection breakage.
In my case it's a purely run-time cycle, so I'd really like to be able
to define it as `require runtime module.a`, with a `runtime` modifier
that would make the module dependency ignored at compile-time (to make
sure I don't add non-reflection calls to it), but respected at link and
= Annotations in module descriptors.
I've read the argument that they complexify things, but I'm not sure
that's true. In practice as long as they don't influence module
resolution at all (and they should not be allowed to), it's no more
complex than having them on packages and types. Sure you have to look at
imports to resolve them, and these imports may only be satisfied once
module dependencies are resolved, but since that's an independent phase
First resolve module dependencies, then process imports and resolve
annotations, and only at the end generate the binary module descriptor
anyway. I don't know of anything in javac's architecture that would make
that hard (which does not mean it does not exist, I'll grant you that).
Annotations there will enable lots of cool stuff from frameworks, much
in the same way they did for types, then packages and last on type uses.
It seems very conservative to forbid them on this new language element
when Java has caught up with allowing annotations in practically every
other location over the years.
Sure they will be able to be retrofitted later via a new class file
attribute, but let's be honest: they will have to, that's pretty much
been Java's evolutionary path, and if it's not hard to implement right
away, why not do it? You know people will beg to have them in Java 10 if
you don't, and you'll likely take criticism for not adding them from the
Now, granted it's always better to add things later than too early, and
it's possible that it'd be too big of a task to do it, or small but not
important enough to warrant a few weeks of work. But IMO it's important,
and I really don't see why it'd be hard to do from the start.
Finally, as I've already stated in the -dev list: congrats for all the
incredible work so far, it's pretty impressive :)
Thanks for any comment.
More information about the jpms-spec-comments