The simplest way is with when/unless vertices which take a boolean input and some other inputs, and which either output the other inputs if the boolean input is true/false, or output nothing.
This gets unwieldy quickly. Consider the ajax pattern:
b = a
.filter1()
.filter2()
.filter3()
.filter4();
if (b.foo()) {
return a.filter5();
} else {
return a.filter6();
}
For visual programming, AFAICT you have to feed `a` downstream twice: once to filter5 and once filter6 in preparation for the conditional branch. That requires one of the following:
* intersecting lines, which are way more visually complicated to read than the code above (and can quickly create visual ambiguities which as a class do not exist in a text-based language)
* one line which loops under the bottom of one of the downstream objects, which is visually distracting (especially if you have more than two branches)
If a is a constant, you can repeat it as inputs to when and unless. If a is a value output by a vertex further back, e.g. output of filter0, then yes you do need edges to when and unless. But the output could be labelled, automatically causing identical labelling at the destinations of the edges, and the lines corresponding to the edges could be hidden until the mouse pointer hovers over the either the output of filter0, the input at when, or the input at unless. It's better demonstrated than described in text, but I hope you follow it.
I'm aware that text is more compact, and understand the various user interface issues (e.g. crossing lines), but there are advantages in representing programs as graphs as opposed to text. One of these is the possibility of very strong type checking while a function is being defined, and I'm currently working on that. Another is that it should be possible to take advantage of homoiconicity: programs are graphs, and graphs are the most general data structures. Also, graphs (i.e. data) can double as programs.
Tutorials for my language can be found here: http://www.fmjlang.co.uk/fmj/tutorials/TOC.html
These are out of date, so suffer from the problem you hinted at above, and they also don't explain my aims. I'll write updates to them when the new type system is properly working.
> It's better demonstrated than described in text, but I hope you follow it.
I do understand that. I thought about implementing something like that for Pd, but in reverse. Programs there tend to look like write-only spaghetti, so it would be helpful to have something like a "spotlight" feature that temporarily removes (or blurs, etc.) the spaghetti that isn't part of a currently selected chain of objects. That would make spaghetti at least navigable with a mouse and some time.
Anyhow, I've never found a dataflow or visual programming that really deals with the ergonomics of writing the code. At some point I just want to do a little math, or a quick conditional, or some such conceptually simple business for which the graph just gets in the way. At that point it's easiest to drop down to some kind of text-based DSL inside a single box and be done with it.
You can call Lisp from my language, and call my language from Lisp. But it will still need a lot more work before I start using it for tasks I currently prefer Lisp for.
That gets to the core of my puzzlement. In terms of dataflow if 'b' is an 'agent' then b.foo() is a message we send to it and then we use the result of b.foo() for further decision making. But in a typical dataflow-diagram data flows in one direction only, there is no link in the diagram from "b" to "result of b.foo()".
A diagram could not (easily?) describe the fact that we get the 'b' from one component and then we send the message "foo" to that component 'b". The data-path from this component to "b" could hardly show in the diagram because "b" is the response to a previous request which could be one of many possible alternative responses.
I'm not sure if I can express or even think about this very clearly but questions like this make me doubt the generality of dataflow as an alternative to current-day programming practice.
Researchers at the University of Manchester and elsewhere built computers which implemented dataflow at the hardware level. The main reason we don't have them is because of Moore's Law: wait a few years and a single CPU will be just as fast as multiple dataflow CPUs.
There are historical reasons why we're programming in text in mostly imperative languages: graphics terminals were either not very good or very expensive, and computers had single CPUs, until quite recently.
Yes boolean function, but wouldn't that have to be a synchronous function, one that the component doing the decision making calls?
I understand that there can be loops in the dataflow and feedback can alter the processing logic of a component at some point in the future - since processing is async, the alteration can only happen in the future.
But when a component needs to make a decision, not alter its processing logic but simply make a choice, can it somehow 'ask a question" from some other component which is part of the data-flow, before it it decide which branch of the data-flow to send data to?
My question is simply is it possible and/or practical to do general programming with only (asynchronous) dataflow between the components/functions/units-of-computation?
Or do we need both synchronous and asynchronous components/functions/agents etc. ?
Dataflow doesn't require synchronization because vertices wait until there are values with the same tag on all their inputs before executing.
In pure tagged dataflow systems, asking a question doesn't happen: programs are directed graphs and like most other programming systems vertices have to wait for values. However, at a higher level, you could have vertices sending messages to one another and receiving answers back.
So computation can not start before I have all the inputs, right? But when I --a vertice-- am doing a computation, I would like to ask for more data from some other component, so that I can complete my calculation. So how could I get such data except as one of my input? But I must have all my inputs already, else I would not be executing.
This would then seem to be at the core of the difficulty with data-flow programming. Once a computation of a node has been activated, it can not ask for help from anybody, because it must have all its inputs present before it starts doing its calculation. Does this make sense ???
The simplest way is with when/unless vertices which take a boolean input and some other inputs, and which either output the other inputs if the boolean input is true/false, or output nothing.