Ok, I've been thinking a lot about composable systems recently, and why I don't find visual programming languages like blueprints very flexible. Wrote up a blog post about it. Basically, I think the value graph is a more fundamental primitive for computation than execution graphs. johnaustin.io/articles/2024/composability-designing-a-visual-programming-language
Just an observation: "value graph" seems analogous to "data flow" in a data flow/control flow sense.
It's similar, but not quite the same. Value graphs are distinct in that a node only ever executes once. It's more akin to a build-graph. Where-as dataflow expects values to be flowing through and nodes have several values over the course of execution.
I've actually been trying to find information about this type of computation, that's not in the context of build system like Bazel, Nix, etc.
For example, if you squint, Git is also a value-graph -- it's a big directed graph where every node holds a single value, which is defined entirely by the node's inputs in a pure way. 🤔
Framed another way, a value graph is like ~provenance~ seen in reverse. Usually when we talk about data provenance its from the perspective of understanding the inputs that produced a particular result. A value graph seems more like starting from the inputs and building up to the result.
Yeah! I like that perspective. Merkel trees, for instance, are also value graphs.
Any piece of the value graph is recoverable if you have the required inputs.
Hmm, this makes me think about visual programming for computational notebooks. Notebook code tends to be focused on the production of specific outputs, not reusable code. Actually the first thing that comes to mind is Ultimate Plumber: github.com/akavel/up
Yeah, many notebooks are just fancy build graphs for data. Very much a value graph. 🙂
IIUC UE Blueprints, which are used as an example of execution graph, are not composable because they force the designer to sequence the nodes. (This is a really cool observation!) Is it possible that Unreal Engine Blueprints are just missing the operations to split & merge signals? I don't know UE well enough but it doesn't seem like a fundamental issue with execution graphs. Am I missing something?
There are operations to split and merge execution wires, such as branch (a split), but the key is there is no split operation for an execution wire that retains the program semantics. A branch semantically changes the program (because it only takes one or the other).
The only semantically sensible "split" operation for an execution wire is to run the nodes in parallel, but that comes with other issues.
Miqula also has value graphs. When I started coding it, I didn't even know blueprints had "execution" wires, how could one do such a hideous thing! To split the execution, I use subnets, for example in a condition, one for true and one that is executed for false. That doesn't solve all problems though, if one would do for example
foo != nullptr ->
foo->IsValid() ->
-> both connected to an && node
you don't need to do subnets, it just does the most sensible thing.
As for side effects, a "regular graph" has none, except assertions (exceptions). To do effects on the world, I use a behavior tree (which is nicely composable). The leaf nodes have regular graphs, which then may contain nodes with side effects (for example to send messages / spawn objects).
Be aware that dataflow programming (with pure functions) is a different model. Most people are used to do the "little robot in your head" that does stuff, one after the other (which is procedural programming). In dataflow, you are wiring dependencies / data together to create new data. You look at the data instead of the instruction.
I'm curious as how you want to design around these problems and how it fit's into an ECS.