4 ms·
It should not be assumed that all visual dataflow languages are similar. They're as varied as textual imperative languages. I'm developing my own dataflow la
by DonaldFisk 9y ago
It should not be assumed that all visual dataflow languages are similar. They're as varied as textual imperative languages.
I'm developing my own dataflow language. Before I began, I had already programmed in many different languages and even designed some of my own languages and dialects.
I came to several conclusions about how visual dataflow should be done, which are different from the approach taken in LabVIEW and other visual dataflow languages such as Prograph, Max, and vvvv.
(1) You should only be able to connect vertices (i.e. functions) when their argument/value types are compatible. This allows you to drag an edge in the right general direction and it will find what to connect to, and also means you can develop a powerful type system with type inference built into the editor. This makes the language safe, but why stop at Hindley-Milner? You can add units (metres, flight levels, dollars, etc.) to real number types. You can give the language dependent types to make it even safer. This would eliminate many errors inherent in developing in other languages. And with a good enough type system, you don't need object orientation.
(2) You can keep the language design simple. You don't need special constructs for things like iteration and conditionals. All you need are vertices (where processing is done), edges (along which values flow), constant values, and enclosures/lambdas.
(3) Iteration can often be abstracted away, by having filters operating on streams of data instead of single data items. It might be worth adding an even higher level, with bidirectional flow between processes or agents.
(4) You can define new types by connecting existing types, just like new functions can be defined by connecting existing functions.
(5) Version control and project management can also be done graphically.
I encountered, and solved, some little-known problems associated with dataflow.
My language isn't yet ready for release. (There's only one of me, not a large team.) I've begun work on dependent types. I need to develop a non-trivial application, for which I have a few ideas. The language also needs a compiler. I'll add version control and project management when there's any commercial interest.
Documentation (which I need to update) is at
http://web.onetel.com/~hibou/fmj/FMJ.html
The papers on the site describe obsolete versions of the language but are still worth reading. The tutorials are more up to date.
- biocomputation 9y agoType safety isn't nearly as simple as you think when it comes to visual programming. There are lots of expressions/functions that need more than "Is the input the right 'type'?" What if the input is the right type, but doesn't have the right count for the expression? How do you deal with that? There are ways of course, but it's not so simple to figure out an elegant, scalable solution. << And with a good enough type system, you don't need object orientation. This might be true, but object orientation is a great way to have a good enough type system. << Version control and project management can also be done graphically. Yes, but it doesn't make it better. For example, in many applications you use a stream of commands and operations in the GUI to get a job done. Operations in the GUI might include things like selections, etc. In visual programming, you might convert that stream of application commands to a set of nodes. It's simple enough at first, but it turns out that things like selections -even selecting a row or two of data- are very, very tedious to do with a visual programming tool (when compared to just doing it in a GUI). What often happens is that you end up duplicating work you'd do in a more standard application by adding dialogs that allow the user to select the data using a standard GUI process and then capture their selection in the node that stores the operation. It's a bit redundant, and it doesn't get around the fact that you're still using a standard GUI to deliver a nice user experience. There's also another issue with visual programming using vertices and edges. When you write code, you might declare a variable. Then later on in the code, you refer to that variable. In a vertex-edge model, there needs to be a connection between the variable declaration and anywhere the variable is used. A vertex-edge model can get ugly and become 'hard to follow' quite rapidly. You have all these inconvenient locations for things, and it can be difficult to try and comprehend a vertex-edge model for some types of code / processes / problems. Some things are definitely better in a vertex-edge model of computation, but a vertex-edge model is not always more user friendly or more productive for many types of work. Source: have worked on very popular visual programming tools. I love visual programming for some problems, but I am aware of the many, many, many pitfalls in this approach.
- DonaldFisk 9y ago> What if the input is the right type, but doesn't have the right count for the expression? I'm not sure what you mean by that. > object orientation is a great way to have a good enough type system. It means different things to different people. The type systems the various kinds of OO provide are subsets of the existing type system, which will soon be extended to support dependent typing. > In a vertex-edge model, there needs to be a connection between the variable declaration and anywhere the variable is used. There are no variables in my language. Some of your other points are valid, but I was already aware of them. Development isn't easy, or I'd have finished it by now.
- biocomputation 9y ago> What if the input is the right type, but doesn't have the right count for the expression? Let's say you have type T. T stores one or more pictures. Let's say you have a vertex in the vertex-edge graph that wants an input of type T, but it also wants a T that stores a specific number of pictures. You can ensure basic type safety by requiring an input T for the vertex, but you cannot ensure expression safety since T satisfies the type safety requirement, but not the requirements of the expression represented by the vertex. In your initial remarks, you mentioned type safety as a relatively easy thing to do, but you neglected to mention expression safety, which is equally important. That a vertex wants T is trivial. That the underlying expression wants a T with very specific properties is decidedly non-trivial. Additional remarks: >> You should only be able to connect vertices (i.e. functions) when their argument/value types are compatible. This also doesn't handle type conversion, which can be exceedingly complicated in visual programming. Should casting be done by vertices? Should you allow implicit type conversions at the vertices? Should the visual programmer be able to choose? I only ask because it is incredibly tedious and frustrating to work with data flow computation tools that say they make things easier, but then have all these super strict requirements about what can be plugged into a particular vertex. Strictness MURDERS expressiveness, and programmers LOVE text because it's super fucking expressive! >> You can keep the language design simple. You don't need special constructs for things like iteration and conditionals. Yes, ideally, you can keep things simple, but you will need to address things like iteration, recursion, and conditionals. Here's why: the REALLY hard part about visual programming is determining how to strike the right balance between the 50,000 foot view and the microscopic view. Many programmers complain (rightly) about visual programming because they feel stuck at the 50,000 foot view and it's incredibly frustrating. On the other hand, doing iteration and recursion with visual programming tools is often very painful. I'll write a bit more about why. To this point, you could make everything work with functions -a 50,000 foot view- but this quickly becomes very inconvenient because it makes it incredibly tedious to solve many types of problems. You'll be forever wrapping things up and trying to decide on the right function boundaries or how large (or small) to make a particular chunk of computation. So then you go ahead and do iteration and recursion. Now your simple vertex-edge design might need a stack, or at the very least, it might need to understand that parts of its computations aren't going to be directly represented by the data flow graph. Now we run into some interesting problems that we can explore using notions from abstract algebra. A data flow graph works best when every element of computation is represented directly in the graph. But if we're using iteration and recursion (using them to prevent us from being inconveniently locked in to a 50,000 foot view of our problem space) then our data flow graph is no longer closed. Problems arise because important (critical) algebraic axioms are no longer satisfied. By closed, I mean that there are elements of computation that are outside the set of data flow nodes (here, the word 'set' refers to the data flow nodes from a set-theoretic perspective). The initial conditions of a for() loop (the initialization expression, condition expression, and loop expression) are represented in the data flow graph, but there aren't any data flow nodes for any subsequent iterations. Since this is the case, how would the user work with them? What about iteration that doesn't use for() loops? Those are much easier, but it doesn't solve the incredibly difficult problems that arise when the data flow graph does not contain all the elements of computation. With our loop situation, we have to look at a very common use case with respect to loops. Often in a loop you'll do some work, but you might also write a value to an unrelated variable or object that you're going to use later. Right? You might initialize something at a particular point in a loop assuming a particular condition is true or false or if the loop counter is a certain value. Since all those loop iterations -elements of computation- are not in the graph, how would you allow a programmer to capture a value during loop execution? This might seem like a nitpick or corner case, but it certainly is not, and these kinds of shortcomings are exactly what programmers mean when they say they don't like visual programming. These are some of the most seriously difficult parts of representing computation with data flow graphs. * I hope you'll post on HN when you have something complete!
- dkersten 9y agoA lot of what you said sounds so obvious, yet I guess its really not since much of that is often missing from visual languages :) (3) this is very true and simplifies things a lot! Functional languages largely do this anyway, so there is precedent for this already in textual languages. Iteration was always a stumbling block for me in my visual-language-design escapades because, while feedback-loops are a natural way of looping in dataflow languages, they added too much complexity (for example, Max's left-to-right execution order -- I really don't like the horizontal positioning of blocks to affect the execution!), but if the language doesn't have loops at all outside of looping "functions" like map, reduce, range etc, that problem largely goes away to be hidden inside the implementation of these constructs. Nice. EDIT: Just re-read the tutorial and I see that you do support recursion. I suppose this is the building block for the higher order constructs? Just like many textual functional languages :) > I've begun work on dependent types. I've looked at your language before (because dataflow languages interest me), but I didn't realise how much was going into the type system. I will be very interested to see a working version when you are ready to release something. Dependent types sound pretty awesome in any language, so I'm especially eager to see how that plays out in a visual language with a real type system!