I always wonder a little bit why dependency-tracking systems that don’t strictly require ordering don’t perform pooling.
It’s one thing if we’re talking about a package manager that builds dependencies from source as they are needed, and therefore must run a particular set of commands in order to make other resources available to a future build. But, if all injected dependencies are available to all their children in the dependency tree, we’re essentially talking about flattening a walk through a graph into a list, right? In other words, if what Guice is doing is creating a list of strings corresponding to classpath elements, and that classpath is made available to every VM we spawn, then circular dependencies are a non-issue because we can check for cycles by searching the already-stored list of dependencies for each new one we find.
(It’s a different matter if we need to make it possible for different versions of a jar with the same canonical name to be used by different classes. I think that could be managed with some reflection-based name mangling.)
This is not a complaint about your solution, of course. This is more of a complaint directed at the authors of these systems, who don’t seem to have realized that they are not operating under the same memory restrictions as Dijkstra & are already storing the information necessary to identify & break cycles in what functionally isn’t really a dependency tree.
(Python imports have a similar kind of solution: a module enters a dictionary of loaded modules before its own imports are handled, and module loading is idempotent, so a circular import is a non-issue.)