Add concept of "interests" to Controllers

Instead of forcing Controllers to expose and manage keys, we can define interfaces that act as interests. It's then possible to query all controllers that are interested (e.g. implement the interface). This would allow the VisibilityController to feed the visibility status to dependencies.

I will have to think more about which direction is better. It may be possible that this interest concept can be used to describe data/processing dependencies as well and help support parallel execution.

Comments (7)

After continued thought, I am having conflict with which control flow is better. Are controllers responsible for getting services interested in what they produce, or do they only need to expose what they produce?

I feel like it's better if they expose it. Perhaps we make that the primary concept. Right now, we do it with keys but I could imagine cleaning it up. For now, it's not a high priority

Simple performance tests show that performing a manual loop over a collection of interfaces is about 2X faster than creating a proxy to perform the looping. Even so, the proxy was pretty fast.

The main performance problem (with both options, since the first was terribly fast) is the problem with call site options. Since they are interfaces, theoretically many controllers could be interested and the JVM can't optimize the call as well. If we have the controller expose resources by a key, then that can be optimized quite easily even though it forces us to expose awkward symbols, such as Key<Map<Entity, Bag<Entity>>>

I'm leaning towards using the proxy'ing, or at the very least moving away from the KVP system. Having an interface describing how something can receive data helps document where the data dependencies are. There can be an annotation describing what a controller produces, and all controllers implementing the interface are receivers.

This could be done via proxy, but it makes it tricky to specify that they should be used to transfer bulk computations and not intermediate results. This might just be a documentation problem.

Another, slightly less flexible approach is to have the process() phase return an interface that can be used to get the provided object, which is then used appropriately by the ControllerManager to pass off to the receivers. This means a controller can only produce one thing, and it must be at the end of computation. Is that so bad, though?

I decided to implement this as a Result type that could supply internal result data to Controller-defined listener interfaces. These results can then be pushed to the ControllerManager for distribution to the interested controllers in the system, allowing the flexibility of the proxy approach but without the performance hits of proxies.