It is happening — Vavr 1.0 will ship as a set of Java modules. In this article I want to share some fundamental design decisions and the rationale for slicing Vavr into modules.
Java modularization skim-through
Vavr 0.9 was already "modularized" in a certain way. We had packages to group related types. Beside that, we utilized static inner classes to implement sealed types and auxiliary classes to hide helper methods on the class level.
We used package-private classes to hide internal utilities and common functionality on the package level (despite of a JDK bug). Both, class level and package level isolation are problematic. With reflection trickery, users were able to circumvent our security mechanisms.
The Java Platform Module System (JPMS aka "Jigsaw"), short jmod, changes quite a bit for us. We are now able to safely define visibility criteria on the module level by exporting only certain packages.
Packages and modules are closely related. By convention, the module name should correspond to the top-most relevant package of an artifact. Let's say, Vavr could be split into two modules, io.vavr.control and io.vavr.collection.
Each module will be shipped as a separate jar, let's say vavr-control-1.0.jar and vavr-collection-1.0.jar. Modules can only declare module dependencies by name, the version is specified on the build level. This is where jars come into play.
Artifacts declare their jar/pom dependencies, modules declare their module dependencies. The developer is responsible for declaring the right dependencies containing the required modules.
The idea is simple: 1 jar ~ 1 module ~ 1 package (subpackages possible). Versions and version dependencies are pulled to the outer-most layer of our boundary layers (physically a .pom file).
The build system is able to identify version conflicts by verifying version ranges during transitive dependency resolution. When loading the application, the module resolution takes place on the module path (the classpath is now obsolete).
The image above shows a fatal decision made by the Java language architects. There exists a 'backdoor', namely Automatic-Module-Name, that allows non-modular jars to be recognized within the module space.
The other way round the backdoor (here: "membrane") is closed. Modules can't exist on the classpath. Because of module dependencies, libraries need to migrate to jmod bottom-up.
It is an organizational problem, libraries need to be modularized in a specific order, requiring dependencies to be already ported. Fear stops progress (if there is no reason, let's wait for the others). This leads to the hen egg problem: no one makes the first step and ports his library to jmod.
👉🏽 Library developers are in charge. Do it the right way, don't use the Automatic-Module-Name backdoor! A good starting point is here.
👉🏽 Java language architects are also in charge. For the sake of Java's health, I really hope that this backdoor will be locked in a future Java release. Why not remove the classpath? It would solve all problems.
I care about my users and my collaborators
Javaslang (now Vavr) was released the same day Java 8 was released (it appeared the next day on Maven Central ;). Now, after more than 4 years, it will happen that Java 8 reaches its end of life (EOL).
Java 11 will be the next LTS release, so I decided to build Vavr 1 upon Java 11. I see no reason to stick with Java 8, nor I see a reason why (enterprise) users, which use Java 9/10 shouldn't upgrade to Java 11.
I'm sure that Java users will adapt to Java's faster release cycles. Once on Java 9, Java's backward compatibility makes it relatively easy to drop-in a new release. I see it as a step to a better Java.
There are two kind of users — the ones that are able to upgrade to Vavr 1 without any hurdles (Java 11 users who use modules and have no conflicting dependencies) and the ones that can't upgrade (because they use Java [8, 11), still use the classpath or have conflicting dependencies).
There aren't any popular libraries on Maven Central that depend on Vavr. So the risk of having conflicting dependencies (that aren't under own control) is low.
Users, that decide to stay on an unsupported Java version, are still able to fallback to Vavr 0.9. There is nothing to loose, Vavr 1 will not introduce any big new features. It is rather the opposite, Vavr will be uncluttered.
The only problem is that Java devs need to use modules. This isn't a Vavr-specific problem. I believe in the module system. Here are some of the benefits for Vavr to migrate to Java 11 and jmod:
- Increased code quality and maintainability — we now are able to enforce specific contribution guidelines, especially when it comes to intra-dependencies
- We are able to ship new features — for example we could leverage the new reactive streams APIs
Update: other benefits are:
- Footprint — tiny distributable runtimes with jlink
- Performane: — fast class loading on the module graph (classpath was linear)
Summed up, I believe in Java modules and want to pave the way for the users of Vavr to benefit from them, too. Just do it.