[jsr294-modularity-eg] Module directive types
Evan Cowden
evan38109 at gmail.com
Thu May 7 14:20:17 EDT 2009
First off, I like the grammar. Very spiffy.
That said, regarding:
> 6. An astute observer might ask how we can reference type literals for types that come from modules. The answer is that we
> defer resolution and validation of these types until after the modules have all been resolved. Obviously, a directive that imports a
> module can't directly reference types from that module. We can generate an error when we encounter these cases.
Should there be module requires directives for the modules containing
the @module types? In the first example, there are imports
(old-style, at the top of the file) for classes in the com.sun.jigsaw,
org.osgi.jsr294 and guice packages, but no reference to them in any
kind of requires statements (@module literals like
com.sun.jigsaw.requires). If they are referenced from requires
statements, the chicken-and-egg problem pops back up.
Alternately, are they bootstrapped from some level below the module
system? I suppose that's legitimate - OSGi puts java.lang on a global
classpath; it could do the same for its @module literals.
Second, regarding interoperability, does this mean that there will be
different syntaxes for core functions for different module systems?
Will I need to take special care to specify essentially different
module files (or one large one with the contents of many) for my
libraries to work in multiple systems? Without at least a minimal set
of shared metadata, this seems to defeat the point of
interoperability. Community projects will have to either target a
specific module system, make a conscious choice to dedicate the
resources to supporting several, or - the more likely one in my
experience - decide to target the least common denominator: none.
This would be pretty annoying alone, but it is not my biggest concern.
This specification deals not just with runtime, but with compile
time. If there is not core, shared metadata for each module system,
then how does the compiler know which types are visible? BJ suggested
last week that there might need to be some form of "compiler plugin"
for each module system that interprets the different grammars.
Which leads me to ask: does this mean I actually have to compile my
code multiple times for different module systems?
Even if they produce the same bytecode, I would still need to perform
multiple compilations to confirm that my metadata is correct.
- Evan Cowden
On Thu, May 7, 2009 at 12:24 PM, Bob Lee <crazybob at crazybob.org> wrote:
>
> I can define a grammar and write a Coin-style proposal for these ideas if you'd like, but Doug told me I should send out the examples sooner rather than later.
>
> First, we have a typical module declaration containing declarative "module directives" for Jigsaw, OSGi, and Guice.
>
> Note: I've always thought that a module declaration would be the perfect place to configure Guice-style dependency injection; we configure type dependencies here, why not object dependencies? The two go hand-in-hand. I originally thought I'd have to wait for Java 8 to build this directly into the language, but if we make the syntax extensible, I could use it for dependency injection configuration right away. If 294 goes with a less type safe approach, I probably won't use it for Guice configuration because our current Java-based approach is more straightforward and type safe.
>
> Example usage:
>
> import com.sun.jigsaw.permits;
> import com.sun.jigsaw.provides;
> import com.sun.jigsaw.requires;
>
> import org.osgi.jsr294.export;
> import org.osgi.jsr294.import;
> import org.osgi.jsr294.classpath;
> static import org.osgi.jsr294.Granularity.*;
>
> import guice.bind;
>
> module m1 {
> // Module directives
>
> // Jigsaw
> provides version: "3.4.5";
> provides alias: "acme" version: "3.4.5";
> requires module: "m2" version: "1.0+";
> permits module: "m3";
>
> // OSGi
> export name: "com.m1" version: "3.4.5";
> import granularity: BUNDLE version: ">1.0";
>
> // "value:" is assumed if only one operand exists.
> classpath { "m2.jar", "api.jar" };
> classpath "./classes";
>
> // Guice
> bind type: Foo.class annotatedWith: Red.class to: FooImpl.class;
> }
>
> Here are the module directive types (defined with @module) used for the module directives above:
>
> package com.sun.jigsaw;
>
> /** Expresses a dependency on another module. */
> public @module requires {
> /** Module name. */
> String module();
> /** Module version query. */
> String version();
> }
>
> /** Versions and optionally renames this module. */
> public @module provides {
> /** Overrides the default module name. */
> String alias() default null;
> /** Module version. */
> String version();
> }
>
> /** Permits another module access. */
> public @module permits {
> /** Module name. */
> String module();
> }
>
> package org.osgi.jsr294;
>
> /** Granularity of import or export. */
> public enum Granularity {
> PACKAGE, BUNDLE;
> }
>
> /** Exports part of this module. */
> public @module export {
> /** Name of package or module. */
> String name();
> /** Specifies the granularity of the export. */
> Granularity granularity() default Granularity.PACKAGE;
> /** Version. */
> String version();
> }
>
> /** Imports part or all of another module. */
> public @module import {
> /** Name of package or module. */
> String name();
> /** Specifies the granularity of the import. */
> Granularity granularity() default Granularity.PACKAGE;
> /** Version range. */
> String version();
> /** Whether or not this import is required. */
> boolean optional() default false;
> }
>
> /** Specifies the classpath of this module. */
> public @module classpath {
> /** Array of path elements. */
> String[] value();
> }
>
> package guice;
>
> /** Binds type T to a more specific version of T. */
> public @module bind<T> {
> Class<T> type();
> Class<? extends Annotation> annotatedWith() default null;
> Class<? extends T> to() default null;
> }
>
> Further ideas:
>
> 1. Like annotations, you can already elide value: in the operand if you only have one operand. Instead, we could always allow you to elide value: for the first operand. With some adjustments to the module directive types, our example module could look like this:
>
> module m1 {
> // Module directives
>
> // Jigsaw
> provides "3.4.5";
> provides alias: "acme" version: "3.4.5";
> requires "m2" version: "1.0+";
> permits "m3";
>
> // OSGi
> export name: "com.m1" version: "3.4.5";
> import BUNDLE version: ">1.0";
>
> // "value:" is assumed if only one operand exists.
> classpath { "m2.jar", "api.jar" };
> classpath "./classes";
>
> // Guice
> bind Foo.class annotatedWith: Red.class to: FooImpl.class;
> }
>
> Instead of forcing the first attribute name to be value, we could use a qualifier instead:
>
> /** Binds type T to a more specific version of T. */
> public @module bind<T> {
> default Class<T> type();
> Class<? extends Annotation> annotatedWith() default null;
> Class<? extends T> to() default null;
> }
>
> 2. We could elide the operand for boolean-typed attributes. For example:
>
> import name: "com.m2" version: ">1.0" optional;
> import name: "com.m3" version: "2" !optional;
>
> Of course, this could result in ambiguity if used for the first operand, in which case we should generate an error and force the user to explicitly specify the name and value.
>
> 3. We could support nested directives. The nested directives would be enclosed in curly braces as the final operand. For example:
>
> bind Foo.class to: FooImpl.class {
> bind Bar.class to: BarThatOnlyFooShouldUse.class;
> };
>
> If the directive type has a value attribute with an array type and the directive has only nested directives as the operand, we wouldn't be able to tell what the user intended, and we should generate an error.
>
> 4. We should support constants. For example:
>
> module m1 {
> String VERSION = "3.4.5";
> provides VERSION;
> export name: "com.m1" version: VERSION;
> }
> The compiler can tell the difference between a constant and a directive based on the type.
>
> 5. Java desperately needs literals for generic types. Guice has worked around their absence with TypeLiteral. I know of two JSRs that have already copied this idea into their own APIs (299 and JAX-RS). I used Class<?> in the directive type examples above, but we probably want to define a new type (something like javax.lang.model.TypeMirror) that can be used easily at compile and run time.
>
> 6. An astute observer might ask how we can reference type literals for types that come from modules. The answer is that we defer resolution and validation of these types until after the modules have all been resolved. Obviously, a directive that imports a module can't directly reference types from that module. We can generate an error when we encounter these cases.
>
> Thanks,
> Bob
>
>
> _______________________________________________
> jsr294-modularity-eg mailing list
> jsr294-modularity-eg at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/jsr294-modularity-eg
>
More information about the jsr294-modularity-observer
mailing list