I finished the refactoring/rewrite from yesterday, and now I'm not quite sure what I want to work on next. As such, I think I'll take a bit more of a break, and try to review everything that's in the code with fresh eyes when I get back to it.
Hm, I've got time, I might as well try to describe what I did.
So, the original interface for the Registry objects was a collection of fluent methods that took some set of arguments (with varying aspects of optional and variable arguments). My goal, which I've stuck to, was to abstract out the Registry object from the act of modifying it. I defined several helper classes that carry minimal amounts of data, and wrote private handler methods to convert instances of helper methods into a modification of the Registry object. I then used singledispatchmethod to collapse the private handlers into a single public fluent interface. However, the existence of this fluent method is an implementation detail, and the intended public interface is to use a build function that accepts an iterable of objects that can be passed to the fluent method, and applies them in order. The private helpers are on the class, because it's their responsibility to maintain the class invariants. (For example, there can't be cycles, because I'm pretty sure a cycle would deadlock the application.)
The key thing I changed was to stop talking about the helper classes as Changes, and calling the fluent function change. Now, they're Requirements and require. The main problem with the original names was that, to the extent that they communicated anything, what they communicated was kind of wrong.
What are they?
They're changes to the registry.
What kind of changes to the registry?
The kinds of changes that can be made to the registry :)
So, if I change the registry with a change, then I get a different registry?
In other words, they're an unspecified kind of change, that doesn't always change things. The name Requirement is somewhat more precise, in that most of them describe some form of dependency link between different types of object. And the name require makes it at least somewhat clear that this is about imposing a requirement, not changing state per se. If you need eggs and then you need eggs, then you still need eggs, you know?
I also renamed the Requirement classes to basically be a sequence of nouns describing their fields, rather than a verb phrase. So, ActionName instead of AddAction, ActionOutput instead of AddTarget, ActionInput instead of AddInput, TargetName instead of NameTarget, and SkippedName instead of SkipName.
Honestly, naming things sucks so much that I'm in no way sure if these new names are good, but I'm pretty confident they're better. There is a bit of weirdness in that, outside of ActionName, Action refers to the Name part of ActionName. Still, they're better than what I had before; maybe I'll come up with some further improvement; we'll see.
Oh, also, I'm a big fan of the high-level noun and verb being merely morphologically related, rather than just the exact same word.
Anyway, probably the next thing I should work on is the virtual environment creation primitive. I'm not quite happy with what the prototype has going on, where I tried to make the individual functions limited in scope, but it honestly only makes sense to call them in one order. Maybe I can work out some form of chained evaluation that makes things a little clearer.
I'll come back to this idea later. For now, I should get ready for bed.