Tuesday, April 8, 2008

JSR 294 Superpackages No More

Alex Buckley posted a mail to the JSR 294 list with was more or less superpackages meeting kryptonite. Though in this case, Alex is in my opinion the good guy! He basically abandons all the unnecessary complexity of the earlier proposal, see my blog about this subject, and comes up with a guiding principle: ''simpler is better''.

The proposal definitely follows that principle. A source file is marked with the word ''module'', just like a package. And that is basically it.

package org.osgi.framework;
module org.osgi.core;

public class AdminPermission {
module void checkCertificate();
...
}

The compiler encodes the module membership in the class file and the VM can enforce that any member attached with the module keyword is not accessible from another member unless it is in the same module. I like it! This can actually be quite useful, also for some of the work we are currently doing in the OSGi Alliance concerning modularization. More detail is needed, but this significant simplification is absolutely in the right direction. There are some rough edges obviously, and I like to weigh my input on them.

Module membership should not be on the source but on the package because it is ''inherently'' safer. Alex argues in his blog that from a technical point of view the best place for module membership declaration is the package (package-info.java), but that from a ''moral'' point of view the class file is better because it allows one to introspect a class file and have all information together. Moral in this case should be seen as the Java philosophy, which he defines as focus on understandability by not letting the compiler infer information, but requiring that the programmer states his intentions clearly, even if this is redundant. I.e.
  
List<List<List<CharSequece>>> list = new ArrayList<List<List<CharSequence>>>()

Is for me harder to read than:
  
List<List<List<CharSequece>>> list = new ArrayList<>()

Because I can trust the compiler to do the right thing and I trust it more with these things than my reading capabilities.

I guess this was also attempted in the JLS, which is also highly redundant. Personally I think this decreases readability because you tend to glaze over sections you already have read 5 times in the previous page. However, this makes it too easy to miss small, highly important differences.

Anyway, I guess consistency is important (though I strongly believe Java would be more readable with ''less'' verbosity, less is more!) but I still very much prefer inherent safe models instead of tool enforced safe models. Why would you introduce a data structure that can be wrong if you can also do it in a way that does not allow those errors? Let me explain this a bit more.

Both Alex and I seem to agree wholeheartedly that packages are first class citizens and that you can not have modules criss-cross through packages. I.e. each package member must have the same module membership. That is, you can not have two classes in the same package that would not belong to the same module. So module membership is therefore not on the type but on its package. Placing the module membership in the source of the type just allows you to create invalid configurations when two sources in the same package would have a different module membership, which will just require more tools to fix them.

Putting the membership on the package would also be a good reason to overhaul the reflection methods of Package. Packages have always been second class citizens in the Java VM, for example you can not find its members. By placing the module membership on the package we have a perfectly good reason to fix these deficiencies.

Additionally, I do have some more wishes for the EDR2. I'll go through them shortly.
  • Module private interfaces - Modules should have module private interfaces. Currently, when you implement an interface, you are forced to make your method public. It must be possible to implement interfaces without forcing you to declare the implementation methods public.
  • Class Loaders - Modules should be able to cross class loaders (with the restriction that packages do not cross class loaders). This will multiple bundles to form one module.
  • Dynamic Membership - I do hope the API will allow dynamic module membership. That is, it should be possible to add and remove packages to/from a module in runtime.
  • Module-Export - I think there is a need to export a member or type to another module only. I.e. if module M1 imports M2, it should be possible for M2 to selectively exports members and types to an importer. This could be done with a keyword or an export on the module. for example:
        M1: Import-Module: M2
public class Foo {
void bar() {
Ok ok = new Ok(); // no problem
new Fail(); } // not visible
}

M2:
export module class Ok { }
module class Fail { }
  • Scoping - Currently type references are only encoded with their name. It will be necessary to add the module name to a type reference, and while we are at it, it would be terrific if this then also encoded the version it was compiled against. This will allow the module system to calculate dependencies instead of relying on the old model of the serendiptity of the classpath. Obviously the module file should be able to change the hard references from versions (1.3.2) to a range (1.3,1.4).
  • Versions First class citizens - As show in the previous example, versions should become first class citizens. I do not think annotations are the right way to go for this.
The previous points are requirements, and not solutions. I do not envy Alex's task to come up with JLS changes. This is the same as open heart surgery.

From an OSGi perspective I think modules as proposed by Alex will be a welcome addition to
the language. I think it addresses some of our uses cases we are working on. Nice work!

Peter Kriens

3 comments:

  1. +1 from me for your suggestions

    ReplyDelete
  2. Hi Peter,

    I have a question regarding JSR 294:

    1- Are there any benefits in extending Java's syntax to support modules, etc. syntactically instead of relying on APIs, etc?

    Last time I remember, extending Java's syntax was not a very successful operation: Generics have a lot of ugly edge cases.

    Even if introduced without any ugly side effects and edge cases, are there any benefits in supporting modules syntactically?

    ReplyDelete
  3. I do think that a module keyword is a good idea because it does provide value. Adding a keyword is possible as long as it is not made a keyword that can appear anywhere in source form. It is quite easy to only interpret the module keyword in places where only keywords are used, thereby not classing with existing code.

    Problems with generics go deeper but they were mostly caused (I was told) because they added wildcards late in the game. However, this should be a warning for JSR 277 (which has 294 folded in) to be extremely careful in the design.

    Kind regards,

    Peter Kriens

    ReplyDelete