Spring's need for optional dependencies
Hi everybody, Juergen from the Spring team here. Quick intro: Spring Framework project lead since 2003, currently focusing on the Spring Framework 4.3 / 5.0 strategy for 2016-2017. After extensive conversations with Mark and Alan at Devoxx Belgium in November, I'd like to summarize our need for optional dependencies from Spring's perspective: Our module arrangement in the Spring Framework project has been pretty stable for about 10 years, splitting the core framework into ~20 modules with a maximum size of ~1 MB each (some are much smaller). The module split is largely based on domain (e.g. aop, beans, jdbc, messaging, orm, web, webmvc, websocket), with each of those modules bringing in a few required dependencies but in particular also optional dependencies which only apply when certain third-party frameworks are used in combination with Spring. Such external frameworks are up for the user to choose; Spring just reacts to their presence and auto-configures itself for integration with those frameworks. For example, when Jackson is present, we automatically register message converters for it; when Joda-Time is present, we automatically register type converters for its value types. This auto-configuration experience is long-established in the Spring world, not only for third-party libraries but also for the JDK: currently between 6, 7 and 8, for example registering java.time converters automatically when JDK 8 is present (along the lines of our Joda-Time support). We have had tight OSGi alignment a few years back, with the framework modules shipping bundle manifests out of the box. We backed out of this again since it turned out to be a pain to maintain on our side, in particular due to the inclusion of version ranges but also due to the extensive length of the resulting manifests in our jar files. There are still variants of the Spring modules which include OSGi metadata but they are maintained by the Virgo project at Eclipse. The canonical Spring Framework jars from Maven Central come without any such metadata at present. Now, as of Spring Framework 5, we aim for being the best possible citizens in a Jigsaw world (while retaining compatibility with JDK 8 at the same time). Our roadmap is closely aligned with JDK 9 there, and we've got a first milestone scheduled for June 2016 now. However, there is a showstopper with Jigsaw: We cannot express the above-mentioned optional dependencies at this point. Note that Spring's auto-configuration experience just implies optionality at runtime; those dependencies are in fact required at compile-time. We'd just like to let our framework modules bootstrap even when none of those optional dependencies are present, dealing with the resulting ClassNotFoundExceptions ourselves (we're simply checking for the presence of certain classes and derive auto-configuration decisions on that basis). This has been working pretty well for us for 10+ years, and we really need this to keep working when declaring our framework modules with Jigsaw-compliant module-info files. After all, we'd love to ship explicit metadata, not keep relying on classpath-like deployment where such implicit access to other modules remains available. The alternative would be decomposing the framework into finer-grained modules which isolate each such dependency into its own module. Aside from bumping up the number of core framework modules from ~20 to >50 that way, we'd also lose most of our auto-configuration experience. Making the user declare such integration modules explicitly is exactly what we're trying to avoid: Instead, declaring a specific Spring module and a specific third-party module should be all that it takes, e.g. spring-webmvc and jackson-databind but no extra spring-webmvc-jackson. So in summary, we have a strong need for some kind of optional marker in Jigsaw dependency declarations, similar to optional resolution in OSGi. Runtime optionality is all that it takes for our purposes: no different treatment at compile time needed, just lenient bootstrapping which lets us run into ClassNotFoundExceptions and deal with those. I imagine other frameworks will be in a similar situation. It'd be my pleasure to discuss our requirements in more detail if needed... Juergen
hg: jigsaw/jake/jdk: Reverting mistakenly submitted patch for sjavac
Changeset: b20c9733ab0b Author:erikj Date: 2015-12-17 11:02 +0100 URL: http://hg.openjdk.java.net/jigsaw/jake/jdk/rev/b20c9733ab0b Reverting mistakenly submitted patch for sjavac ! make/gendata/GendataBreakIterator.gmk
hg: jigsaw/jake: Reverting mistakenly submitted patch for sjavac
Changeset: 89666c1768e8 Author:erikj Date: 2015-12-17 11:02 +0100 URL: http://hg.openjdk.java.net/jigsaw/jake/rev/89666c1768e8 Reverting mistakenly submitted patch for sjavac ! make/common/JavaCompilation.gmk ! test/make/TestJavaCompilation.gmk
Wrap-up on Ceylon and Java 9
Hi, I've summed up the work I did on making Ceylon modules compile and run on Java 9 here: http://ceylon-lang.org/blog/2015/12/17/java9-jigsaw/ Feel free to give it a try :) Cheers and thanks for the help!
Re: Spring's need for optional dependencies
Hi, On 12/17/2015 12:03 PM, Stephen Colebourne wrote: And here are the threads for Joda projects, which also need optional dependencies: http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005462.html http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005638.html Note, I do not consider command line flags to be acceptable as a solution. Stephen On 17 December 2015 at 09:41, Stephane Epardaudwrote: As I already mentioned, we also have the need for this in Ceylon, for the same reasons. Dependencies are required at compile-time but optional at run-time, based on detection: if it's there fine, if not then no problem. The only problem I see with optional dependencies at runtime is as follows... If "requires optional X" semantic was to include the module X in configuration if it could be found with module finder (on -modulepath), otherwise not, then the established configuration would not only be dependent on command-line arguments, but also on the content of module directories. If there was a common directory used as a repository for various modules, you would not be able to opt-out of using a particular module if it was declared as optional dependency and included in the modulepath. So instead of assembling command-line arguments (-addmods ...), you would be forced to assemble private module directories for each particular configuration. Contrasting this with what we have now, the classpath: you have to declare that you use a particular optional dependency on command line, by mentioning it on the -classpath. And when you do that (assemble a -classpath command line argument), the configuration does not even check that it really is there. If the .jar file isn't there, it is simply ignored. So I think the safe "requires optional X" semantic would have to be such that it acts as two descriptors: requires X - at compile time nothing - at runtime (no attempt to find the module and add it to configuration) You would still have to put -addmods X to command line, but then you would have a total control over configuration from command-line only. Optional dependencies basically then just reduce to a means to have two different descriptors: one for compile-time and one for run-time, where run-time has a sub-set of requires from compile-time descriptor. It can be done now (with separate compilation), but it would be convenient to have a single descriptor with two scopes of requires. Regards, Peter
Re: Spring's need for optional dependencies
Peter, thanks for your comments. I differ in that I don't see any problems with optional dependencies. Right now, like in Spring, optional features are enabled with a Class.forName() runtime check; if ClassNotFoundException is captured, the feature is unavailable. I expect that coding pattern to continue with optional dependencies. Libraries know how to check if a class is available and fallback to another plan when it's not. Regarding your concern on the command line, I am not sure if people will be using the command line often. I expect tools to eventually read the Module Descriptors and assemble the correct list of modules. I believe Maven is currently investigating something similar right now. Currently, Jigsaw only reads a module directory, but eventually individual jars will be able to be listed. Just let tools solve this problem. Cheers, Paul On Thu, Dec 17, 2015 at 10:58 AM, Peter Levartwrote: > Hi, > > > On 12/17/2015 12:03 PM, Stephen Colebourne wrote: > >> And here are the threads for Joda projects, which also need optional >> dependencies: >> >> http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005462.html >> >> http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005638.html >> >> Note, I do not consider command line flags to be acceptable as a solution. >> >> Stephen >> >> >> On 17 December 2015 at 09:41, Stephane Epardaud wrote: >> >>> As I already mentioned, we also have the need for this in Ceylon, for >>> the same reasons. Dependencies are required at compile-time but optional >>> at run-time, based on detection: if it's there fine, if not then no >>> problem. >>> >> > > The only problem I see with optional dependencies at runtime is as > follows... > > If "requires optional X" semantic was to include the module X in > configuration if it could be found with module finder (on -modulepath), > otherwise not, then the established configuration would not only be > dependent on command-line arguments, but also on the content of module > directories. If there was a common directory used as a repository for > various modules, you would not be able to opt-out of using a particular > module if it was declared as optional dependency and included in the > modulepath. > > So instead of assembling command-line arguments (-addmods ...), you would > be forced to assemble private module directories for each particular > configuration. > > Contrasting this with what we have now, the classpath: you have to declare > that you use a particular optional dependency on command line, by > mentioning it on the -classpath. And when you do that (assemble a > -classpath command line argument), the configuration does not even check > that it really is there. If the .jar file isn't there, it is simply ignored. > > So I think the safe "requires optional X" semantic would have to be such > that it acts as two descriptors: > > requires X - at compile time > > nothing - at runtime (no attempt to find the module and add it to > configuration) > > You would still have to put -addmods X to command line, but then you would > have a total control over configuration from command-line only. > > Optional dependencies basically then just reduce to a means to have two > different descriptors: one for compile-time and one for run-time, where > run-time has a sub-set of requires from compile-time descriptor. It can be > done now (with separate compilation), but it would be convenient to have a > single descriptor with two scopes of requires. > > Regards, Peter > >
hg: jigsaw/jake/langtools: Update Layer API to make it easy to create layers without needing to provide
Changeset: 1def640be25b Author:alanb Date: 2015-12-17 17:00 + URL: http://hg.openjdk.java.net/jigsaw/jake/langtools/rev/1def640be25b Update Layer API to make it easy to create layers without needing to provide class loaders ! src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java ! src/jdk.compiler/share/classes/com/sun/tools/javac/util/ModuleWrappers.java
hg: jigsaw/jake/jdk: 2 new changesets
Changeset: 74b5562461a5 Author:alanb Date: 2015-12-17 17:00 + URL: http://hg.openjdk.java.net/jigsaw/jake/jdk/rev/74b5562461a5 Update Layer API to make it easy to create layers without needing to provide class loaders ! src/java.base/share/classes/java/lang/ModuleClassLoader.java ! src/java.base/share/classes/java/lang/reflect/Layer.java ! src/java.base/share/classes/java/util/ServiceLoader.java + src/java.base/share/classes/jdk/internal/misc/Loader.java + src/java.base/share/classes/jdk/internal/misc/LoaderPool.java ! src/jdk.jlink/share/classes/jdk/tools/jlink/TaskHelper.java + test/jdk/jigsaw/reflect/Layer/BasicLayerTest.java + test/jdk/jigsaw/reflect/Layer/LayerAndLoadersTest.java - test/jdk/jigsaw/reflect/Layer/LayerTest.java + test/jdk/jigsaw/reflect/Layer/src/m1/module-info.java + test/jdk/jigsaw/reflect/Layer/src/m1/p/Main.java + test/jdk/jigsaw/reflect/Layer/src/m1/p/Service.java + test/jdk/jigsaw/reflect/Layer/src/m2/module-info.java + test/jdk/jigsaw/reflect/Layer/src/m2/q/Hello.java + test/jdk/jigsaw/reflect/Layer/src/m3/impl/ServiceImpl.java + test/jdk/jigsaw/reflect/Layer/src/m3/module-info.java ! test/jdk/jigsaw/reflect/Proxy/ProxyClassAccessTest.java ! test/jdk/jigsaw/reflect/Proxy/ProxyLayerTest.java ! test/jdk/jigsaw/scenarios/container/src/container/container/Main.java ! test/jdk/jigsaw/util/ServiceLoader/ServicesTest.java Changeset: 2cf87b107845 Author:alanb Date: 2015-12-17 17:44 + URL: http://hg.openjdk.java.net/jigsaw/jake/jdk/rev/2cf87b107845 Add new entry point for rmic tool ! make/launcher/Launcher-jdk.rmic.gmk + src/jdk.rmic/share/classes/jdk/rmi/rmic/Main.java
Re: MethodHandles.Lookup and modules
Hi John, On 12/11/2015 12:38 PM, John Rose wrote: That's fine. There are two main use cases for Lookup.in, neither of which require the tracking of long chains of L.in(A/B/C…). A. Agent with full-power lookup wants to invoke another agent with the lookup, but wants to limit access, because he doesn't fully trust the other agent. He does a single L.in(A) to a remote-enough type A, creating a non-full-power lookup. (Note: Picking A is sometimes non-trivial. This might be an API flaw.) B. Agent with full-power lookup wants to get access to private nestmate in A. He does a single L.in(A) where LC and A are in the same package member. This works around differences between access checks at JVM and JLS levels, just as the package-private accessor methods from javac do. (Yuck!) Focusing on case A, please consider the following design: - PUBLIC lookup mode means: Any 'public' type of any package exported unconditionally by the package's module. - QUALIFIED lookup mode means: Any 'public' type of any package exported in qualified fashion by the package's module to the lookup class's module. - MODULE lookup mode means: Any 'public' type of any package in the lookup class's module. (Sidebar: QUALIFIED is split from MODULE primarily to be explicit about access rights and secondarily to support more precise slicing of access rights in a future MethodHandles.Lookup API. Example: give me a lookup object to access the types in this module that offer a contract, i.e. are declared 'public' without regard to exported-ness. Example: give me a lookup object to access the types outside this module which are exported to it by its friends.) - Start with an arbitrary class in an arbitrary module calling MethodHandles.Lookup.lookup() to get a "full power" lookup object L. L's lookup modes are PUBLIC + QUALIFIED + MODULE + PROTECTED + PACKAGE + PRIVATE. - The arbitrary class obtains a Class object representing class A, then calls L.in(A): -- If A is in a different module than L's lookup class, then the resulting lookup object has lookup mode PUBLIC. -- If A is in the same module as L's lookup class, but a different package, then the resulting lookup object has lookup mode PUBLIC + QUALIFIED + MODULE + PROTECTED. (#include some stuff about actually accessing protected members outside A's package.) -- If A is in the same module as L's lookup class, and in the same package, but A is a different class than L's lookup class, then the resulting lookup object has lookup modes PUBLIC + QUALIFIED + MODULE + PROTECTED + PACKAGE. -- If A is the same class as L's lookup class, then the resulting lookup object has lookup modes PUBLIC + MODULE + PROTECTED + PACKAGE + PRIVATE. - L.in(A) succeeds (returns a lookup object) regardless of whether its caller is in a module that reads A's module. Only when find* is called on a lookup object is there a check that the caller-of-find*'s module reads the module containing the lookup class embodied by the lookup object. It's easy for the caller-of-find* to pass the check by calling addReads(...) just before calling find*. (Sidebar: Separately, the module containing the lookup class embodied by the lookup object had better have readability to other modules in order for find* to look up [ctors, methods, and fields of] classes in those other modules.) Should there be a way to build a lookup, for two modules M1/M2, which reads those names of M2 which M1 can read, except no internals of M1? I wonder if such a thing would be useful? Probably not. But it would be useful to have a lookup in a module M1 which can read the exports of *every* M2 that M1 can see, except no M1 internals. (This includes the unconditionally exported public names of M1.) This would be a Lookup with an LC in M1 and flags of PUBLIC only. The difference between these two paragraphs is hard to discern. The first paragraph seems to fix M1 and M2 while the second paragraph fixes M1 and varies M2, but there's also a switch from "M1 can read" to "M1 can see". Modules read modules, classes see classes, types access types. Can you restate? Alex
hg: jigsaw/jake/jdk: Update definePackage @throws IAE javadoc and builtin loader to handle IAE case
Changeset: ec4fb763dbce Author:mchung Date: 2015-12-17 12:10 -0800 URL: http://hg.openjdk.java.net/jigsaw/jake/jdk/rev/ec4fb763dbce Update definePackage @throws IAE javadoc and builtin loader to handle IAE case ! src/java.base/share/classes/java/lang/ClassLoader.java ! src/java.base/share/classes/jdk/internal/misc/BuiltinClassLoader.java
Result: New Jigsaw Committer: Jan Lahoda
Voting for Jan Lahoda [1] is now closed. Yes: 6 Veto: 0 Abstain: 0 According to the Bylaws definition of Lazy Consensus, this is sufficient to approve the nomination. Jonathan Gibbons [1] http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005534.html
Re: MethodHandles.Lookup and modules
On Dec 14, 2015, at 7:02 AM, Alan Batemanwrote: > ... > In graph terms then the vertices in the readability graph are the modules. A > directed edge from module M to module M2 means that M reads M2. The graph is > mutable in that edges can be added via the API at runtime. Code in module M > can add a directed edge so that M reads M2. Code in M2 might add a read edge > in the other direction. Code in a module cannot change other modules: M > cannot change M2 so that M2 reads M or M3 or any other module. For > completeness then I should say that edges are only removed when the the > vertices (modules) they connect are GC'ed. This graph of the "M1 READS M2" relation is a handy visualization. How does it interact with exports, and the unconditional/qualified distinction? Let me try to figure it out… I guess the READS edges would be labeled by the types that flow across them. Let's say TYPES_VIA(M1 READS M2) = {m2pkg.P, m2pkg.Q, …} is the set of types (all public) from M2 that M1 can read. And, I guess that TYPES_VIA(M1 READS M2) contains exactly M2's unconditional (non-qualified) exports, plus M2's qualified (non-unconditional) exports to M1. Which is not to say that code in M1 actually uses all those types, but it could. And it can't use anything else from M2. Yes? (TIA) >> A. Agent with full-power lookup wants to invoke another agent with the >> lookup, >> but wants to limit access, because he doesn't fully trust the other agent. >> He does a single L.in(A) to a remote-enough type A, creating a >> non-full-power lookup. >> (Note: Picking A is sometimes non-trivial. This might be an API flaw.) >> >> B. … > > Case A is where our current approach might be too limited. This may be tied > into the discussion point as to how to choose A. If A is in the same module > as LC then it's as before. However if code in named module M creates a > full-power lookup and chooses A in another module M2 then the resulting > L.in(A) has zero access. > > It wouldn't be hard to change this to allow PUBLIC be preserved so that > L.in(A) would at least allow access to public types in packages that are > exported unconditionally by the modules that m(A) reads. Would that increase > usefulness? Would such cases be cases where PL is equally useful? Hmm… What PL does is ignore the M1 READS M2 graph, or (equivalently) adds temporary edges as needed to access unconditionally exported names. (Anticipating a reply to Alex's message of today…) Possibly, a PUBLIC-type lookup QL would only be able to see types readable by M1=MODULE(QL.LC). That is, QL (possibly) would respect the READS graph as it exists, and not attempt to extend it on the fly like PL (publicLookup) does. Points about this: 1. Such a lookup QL is stronger than PL in that it might see qualified exports readable by M1. (This would be Alex's QUALIFIED mode, which I like.) But QL would also be weaker than PL, in that it would respect the existing READS edges, and not create temporary edges like PL does. (Am I right that this is a real distinction? I'm coming up to speed here!) 2. The restriction on QL is interesting in theory but maybe not in practice. After all, if a nosy browser of modules wants to see an unconditional export from M2, he doesn't need to bother with a QL in M1; he just uses PL. 3. How much do we all care that modules are completely leaky in their unconditional (unqualified) exports? It's really helpful that a module can hide its unexported types (and seal them up for optimization, etc.). It's also helpful that modules can use qualified exports to be friends. Do we (can we) discourage Java apps from browsing modules for their unconditional exports? I'm guessing "no (and no)" is the answer. If that's true then there is little reason to limit any QL's ability to be a superset of PL. 4. OTOH, suppose a user is trying to be a good citizen of Module World, and wants to avoid accessing unqualified exports in the absence of a previously declared READS edge. It that a real use case? If so, then having a QL that throws an error if a READS edge is missing will help debug the READS graph, instead of sweeping errors under the rug. From this point of view, usage of PL would be slightly bug-prone, since PL ignores the carefully constructed READS graph. Bottom line: If the READS graph provides useful restrictions, even for unconditional (unqualified) exports, then it would be useful to have a QL (not stronger than PL) which emulates a lookup from some M1, including the *inability* to read any old export.. Bottom line #2: I like Alex's suggestion of having a QL which mimics the lookup "perspective" of public types visible to some M1, but external to M1. (This QL would be not weaker than PL, as well as not stronger.) I was kind of fishing for that extra "mode bit", and I think we caught something. Bottom line #3: If we believe that QL should be incommensurable with PL, it follows that PL has unique
Re: Spring's need for optional dependencies
And here are the threads for Joda projects, which also need optional dependencies: http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005462.html http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005638.html Note, I do not consider command line flags to be acceptable as a solution. Stephen On 17 December 2015 at 09:41, Stephane Epardaudwrote: > As I already mentioned, we also have the need for this in Ceylon, for > the same reasons. Dependencies are required at compile-time but optional > at run-time, based on detection: if it's there fine, if not then no problem.
Re: MethodHandles.Lookup and modules
On Dec 17, 2015, lookup modest 12:44 PM, Alex Buckleywrote: > > … please consider the following design: > > - PUBLIC lookup mode means: > > Any 'public' type of any package exported unconditionally by the package's > module. Accessing those is the uniquely special power of PL=publicLookup, which cavalierly ignores the READS graph, and therefore does not need to be computed relative to anything. For clarity I would like to give this thing a special name, UNCONDITIONAL. UNCONDITIONAL(M1) = { T, if IS_PUBLIC(T), in PACKAGE_TYPES(P), if IS_UNCONDITIONALLY_EXPORTED(P), for P in MODULE_PACKAGES(M1) } UNCONDITIONAL() = UNION { UNCONDITIONAL(M1), for M1 in ALL_MODULES } > - QUALIFIED lookup mode means: > > Any 'public' type of any package exported in qualified fashion by the > package's module to the lookup class's module. QUALIFIED(M1) = { T, if IS_PUBLIC(T), for T in PACKAGE_TYPES(P), if IS_CONDITIONALLY_EXPORTED(P, M1), for P in MODULE_PACKAGES(M2) } L_QUALIFIED(L) = QUALIFIED(TYPE_MODULE(L.LC)) N.B. This leaves the following related expression as an orphan (cannot be recomposed from the others): - READABLE(M1) lookup mode means: Any 'public' type of any package exported in qualified or unqualified fashion by the package's module to the lookup class's module. READABLE(M1) = QUALIFIED(M1) + UNQUALIFIED(M1) where UNQUALIFIED(M1) = UNION { UNCONDITIONAL(M2), for M1 READS M2 in READS_GRAPH } Note that UNQUALIFIED(M1) is a subset of UNCONDITIONAL() for all M1. These classifications are nicely disjoint, so the eventual nesting behavior arises by disjoint union. If PUBLIC/UNCONDITIONAL is the wrong ground-level default (my point today), then READABLE(M1) is more useful than QUALIFIED(M1). > - MODULE lookup mode means: > > Any 'public' type of any package in the lookup class's module. MODULE(M1) = { T, if IS_PUBLIC(T), for T in PACKAGE_TYPES(P), for P in MODULE_PACKAGES(M1) } MODULE(M1) is disjoint from READABLE(M1) and QUALIFIED(M1), but not from PUBLIC/UNCONDITIONAL. For lookups we can derive things like: L_UNCONDITIONAL(L) = UNCONDITIONAL() L_READABLE(L) = READABLE(TYPE_MODULE(L.LC)) L_MODULE(L) = MODULE(TYPE_MODULE(L.LC)) > (Sidebar: QUALIFIED is split from MODULE primarily to be explicit about > access rights and secondarily to support more precise slicing of access > rights in a future MethodHandles.Lookup API. Example: give me a lookup object > to access the types in this module that offer a contract, i.e. are declared > 'public' without regard to exported-ness. Example: give me a lookup object to > access the types outside this module which are exported to it by its friends.) This is a useful building block, but needs to be associated with UNQUALIFIED(M1) or UNCONDITIONAL(). > - Start with an arbitrary class in an arbitrary module calling > MethodHandles.Lookup.lookup() to get a "full power" lookup object L. L's > lookup modes are PUBLIC + QUALIFIED + MODULE + PROTECTED + PACKAGE + PRIVATE. Or, in terms of my previous message, it could omit PUBLIC/UNCONDITIONAL and be READABLE + MODULE + PROTECTED + PACKAGE + PRIVATE This means that PL is not a subset of all other non-trivial lookups. It also means that the read-graph blindness stays unique to PL. > - The arbitrary class obtains a Class object representing class A, then calls > L.in(A): > > -- If A is in a different module than L's lookup class, then the resulting > lookup object has lookup mode PUBLIC. If we started with READABLE instead of PUBLIC/UNCONDITIONAL, the resulting lookup L.in(A) would be degenerate (call it NOACCESS mode). This surprise would be the cost of recognizing and enforcing the uniqueness of PL. At the present moment, I think this is the best way to go. PL.in(A) would continue to be UNCONDITIONAL (as long as A is in UNCONDITIONAL()). (There's no union or non-empty intersection of L and PL, if they do incommensurate things.) For PL I think this simple rule suffices: > -- If L is PUBLIC, then L.in(A) is also PUBLIC, unless A is inaccessible to > L, in which case L.in(A) has no access. In fact, a PUBLIC/UNCONDITIONAL lookup is always the result of publicLookup() itself or a derivative of publicLookup() via a chain of L.in(A). BTW, there are three global rules that interact with these rules: -- If A is not accessible to L's lookup class, using L's lookup modes, then L.in(A) has no access. -- In all cases, the lookup modes of L.in(A) are a subset of (or equal to) the lookup modes of L. -- In all cases (assuming no concurrent change in schemata) the set of names accessible to L.in(A) is a subset of (or equal to) the set of names accessible to L. The third rule is a very broad requirement with all sorts of detailed implications. All the other rules are designed to add up to the this main rule. > -- If A is in the same module as L's lookup class, but a different package, > then the resulting lookup object has lookup mode PUBLIC +