Vavr: Automatic module name missing since change to gradle

Created on 3 Apr 2020  ·  12Comments  ·  Source: vavr-io/vavr

I noticed when upgrading to 0.10.2 that the module name no longer works. It is in the maven build but I don't see it being set in gradle.

error: module not found: io.vavr
  requires io.vavr;
                            <Automatic-Module-Name>io.vavr</Automatic-Module-Name>
bug «cross cutting»

Most helpful comment

I fixed all previous comments accordingly :)

All 12 comments

Yes, that's right. It did not work at all the way we implemented it. There were overlapping namespaces. We will fix that in the upcoming version 1.0

The automatic module name was not removed when switching from Maven to Gradle. It was removed in Maven by this commit: https://github.com/vavr-io/vavr/commit/fa48892f75ce8be4237cc4c2f28572d7026969cd. @danieldietrich , could you elaborate a bit the overlapping problem? Otherwise, since 1.0.0 development, I see modules moved to different Git repositories (vavr-match, vavr-benchmark, ...). So I guess there won't be overlapping problem anymore in this repository. Therefore, I send a simple patch to fix it (https://github.com/vavr-io/vavr/pull/2576).

This is really problematic.
Changing a module name is like changing the package names of classes.
And removing the Automatic-Module-Name without replacing it by module-info.java with the same module name means the module name is derived from the JAR file name and thus would be vavr and vavr-match.

The major problem with this is not that if you update vavr in your project that you simply have to change your own module-info.java.
The main problem is this:

  • library A depends on vavr 0.10.1 and thus has requires io.vavr in its module-info.java
  • project B uses vavr in its own code and uses library A, but project B is modern and decides to use 0.10.2
  • any decent dependency management system like Gradle will pick 0.10.2 and not include 0.10.1 of course
  • now the requires dependency for library A is not fulfilled anymore, as in 0.10.2 the module name changed to vavr

Due to that, module names should be consideres as immutable as package names are.
If you change it, you break downstream projects.
If you need to change it, you better also change all package names and release new artifacts at different coordinates like commons-collections vs. commons-collections4.

I agree that a module name should not be changed. However, I see the name as bug that needs to be fixed.

We violate the rule that a module name must be prefix-free: A module name must not be prefix of another module name.

No one screamed up to this day. This tells me that either no Vavr user is using modules and/or no Vavr user is using other Vavr projects than the core project.

| Project | Automatic Module Name | Released |
| --- | --- | --- |
| vavr | io.vavr | Yes |
| vavr-beanvalidation2 | io.vavr.beanvalidation2 | Yes |
| vavr-gson | io.vavr.gson | Yes |
| vavr-gwt | io.vavr.gwt | Yes |
| vavr-hibernate | (none, should be io.vavr.hibernate) | Yes |
| vavr-jackson | io.vavr.jackson | Yes |
| vavr-match | io.vavr.match | No |
| vavr-match-processor | io.vavr.match.processor | No |
| vavr-render | (none, should be io.vavr.render) | Yes |
| vavr-kotlin | io.vavr.kotlin | Yes |
| vavr-test | io.vavr.test | Yes |

I ask myself why all the Vavr projects compile that depend on the Vavr core lib. Because the automatic module names collide...

I would just change the module name io.vavr to io.vavr.core and we are done. If projects fail which depend on the module name, they need to be fixed. I don't think there are many projects out there where this is the case. At least not on maven central. All others are under private control and can be easily fixed. This is a practical approach.

To resolve the other conflict, I will rename io.vavr.match to io.vavr.match.annotation.

I'm curious, where is the no-prefix rule defined? I'm not super familiar with JPMS yet, but I never heard of that rule. I thought module names are self-contained, like package names are too.

I've double checked it (without trying it out).

Stephen Colebourne writes on his blog:

Creating a module with a particular name takes ownership of that package name and everything beneath it.
As the owner of that namespace, any sub-packages may be grouped into sub-modules as desired so long as no package is in two modules.

@nicolaiparlog writes in his book "The Java Module System", Ch. 3.2.2 "Experimenting with unreliable configurations":

Interestingly, the compiler shows an error only if the module under compilation can access the split package in the other module. That means that the split package must be exported.

Furthermore, Nicolai writes on his blog about "Split Packages":

This one is a little tricky… To enforce consistency a module is not allowed to read the same package from two different modules. The actual implementation is stricter, though, and no two modules are allowed to even contain the same package (exported or not).


Putting this all together, my understanding is that it is considered as best practice that a module

  • has the name of the base package
  • takes ownership of all sub-packages

The latter is not the case for us. But I think it isn't mandatory. Namely, it is allowed that

  • package io.vavr is exported by module io.vavr
  • package io.vavr.match is exported by module io.vavr.match

I guess, we do not to change anything then and are able to use the module names that are already released.

Creating a module with a particular name takes ownership of that package name and everything beneath it.
As the owner of that namespace, any sub-packages may be grouped into sub-modules as desired so long as no package is in two modules.

As far as I understood that blog it is just a convention the author suggests to be used.
And it does not forbid one module name being a prefix of the other.
It just says if you use de.my.foo, then you claim that you own de.my.foo and all module names starting with de.my.foo..
Just like it is also common practice with package names.
Also there, de.my.foo and de.my.foo.bar are not really related.
They are technically standalone self-contained packages.
Calling the latter sub-package of the former is colloquial, but technically not correct.

The only rule JPMS imposes as far as I'm aware is, that no two module names must be the same on the same module path and that one package containing classes must not be contained in two modules on the module path.
So having a module with de.my.foo.Foo and a module with de.my.foo.bar.Bar is ok, but having a module with de.my.foo.bar.Bar and another with de.my.foo.bar.Baz is not ok.

A common practice and good rule of thumb is to use reverse DNS names like is common for packages, but actually any module name that is as unique globally as possible would be ok.

Interestingly, the compiler shows an error only if the module under compilation can access the split package in the other module. That means that the split package must be exported.

Yes, also this is only about split packages. (my Bar and Baz example).
It has nothing to do with module names.

This one is a little tricky… To enforce consistency a module is not allowed to read the same package from two different modules.

And again, this only talks about split packages, not module names.

Putting this all together, my understanding is that it is considered as best practice that a module

  • has the name of the base package

At least it is a good way to reach as unique as possible module names. If you chose good package names that is. :-)

  • takes ownership of all sub-packages
    The latter is not the case for us. But I think it isn't mandatory.

Is it not?
Is anyone else releasing something starting with io.vavr.?
Anyway, as you correctly recognized, all these are only suggested conventions or best practices.
The only rules I'm aware of is uniqueness of module names and forbidden split packages.

Namely, it is allowed that

  • package io.vavr is exported by module io.vavr
  • package io.vavr.match is exported by module io.vavr.match

Yes, this is absolutely no problem as far as I know, as io.vavr and io.vavr.match are completely separate packages. If you would have a class io.vavr.Foo the io.vavr.match module, that would be bad and a forbidden package split.

I guess, we do not to change anything then and are able to use the module names that are already released.

I cannot tell, as I didn't look into the artifacts, but you can tell for sure or easily check it by looking into the artifacts. :-)
You can also add a simple test that adds all modules to a module path and tries to start something. If there are split packages it should complain.

Is it not?
Is anyone else releasing something starting with io.vavr.?

I meant that, in the best case, one module has the ownership of a package. In the case of two modules de.my.foo and de.my.foo.bar with packages of the same name, this is not the case. Even if we have the control over these two modules.

But nevertheless, it is just a convention.

I cannot tell, as I didn't look into the artifacts, but you can tell for sure or easily check it by looking into the artifacts. :-)

Fortunately, I already did it and posted the result in the table above. It looks fine.

You can also add a simple test that adds all modules to a module path and tries to start something. If there are split packages it should complain.

Here is a little test (using coursier for fetching dependencies):

jshell \
  --module-path $(coursier fetch -p io.vavr:vavr:0.10.0):$(coursier fetch -p io.vavr:vavr-jackson:0.10.0) \
  --add-modules io.vavr,io.vavr.jackson \
  --startup ~/.jshell/vavr.jsh

Where ~/.jshell/vavr.jsh is:

import java.io.*
import java.math.*
import java.net.*
import java.nio.charset.*
import java.nio.file.*

// import java.util.*
// import java.util.concurrent.*
import java.util.concurrent.TimeUnit

import java.util.function.*
import java.util.prefs.*
import java.util.regex.*

// import java.util.stream.*

import io.vavr.*
import io.vavr.collection.*
import io.vavr.concurrent.*
import io.vavr.control.*

import static io.vavr.API.*

It works for v0.10.0!

Screenshot 2020-05-15 at 00 43 19

The automatic module name disappeared in v0.10.1 v0.10.2, I will fix that in the upcoming patch release v0.10.3.

The upcoming v1.0.0 will have the same module name io.vavr.

Thanks, that helped :)

I think this is not done yet. We need the 0.10.3 backport as you mentioned @danieldietrich :

The automatic module name disappeared in v0.10.1 v0.10.2, I will fix that in the upcoming patch release v0.10.3.

@mincong-h you are right, I was a little bit too early. In parallel I worked on the backport: #2583

And just for future readers that care.
The only "faulty" release is 0.10.2.
In 0.10.1 the Automatic-Module-Name is still set. :-)

I fixed all previous comments accordingly :)

Was this page helpful?
0 / 5 - 0 ratings