Instead of working on node-graph more, I made some unimportant breakthroughs on internal code structure for Structured Data.
I then procrastinated writing this, so it's late now and I'll try to be brief:
One of the patterns I use a few places in Structured Data, and in other code, is to create a stack of items, and some means of processing those items, which can either pass them to code outside the scope of the stack, or create more items to put on the stack. I usually implement this with a while loop, but it kind of seemed like it should be possible to represent the stack as an iterator, from the perspective of the out-of-scope code. It took me until today to figure out how to do that.
The basic idea is that the processing function has to return one of two things: an instruction to yield a value, or a sequence of values to put on the stack. I'm representing these as different types, because I can, and I have questionable habits around reducing cyclomatic complexity. Basically, one type holds a value and yields it, the other holds a sequence, puts it on the stack, and yields nothing. (I also added special-case support for None because it looks less cluttered that way.)
The way this shakes out, the stack and while-loop are still created, but they're confined to a single module. Consumers just have to define the processing function, which only sees a single item, and use it in a for loop, where the body of the for loop is all of the stuff that the containing function actually cares about, like containers to get values accumulated in.
I razzed myself a little for lowering cyclomatic complexity so much, but there are some things about this that make me think it is probably actually better. Chief among them is the fact that the high-level code has lower parameter counts in the function definition: less state has to be bundled together at a given level.
Anyway, that's what I did tonight. Good night.