4 ms·
> 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
by 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!
- DonaldFisk 9y ago> it also wants a T that stores a specific number of pictures. With dependent typing, not only can you specify the size of structures such as arrays and lists, the type system has access to those properties at run time even in dynamically created structures, so when you extract an element by position, you have to index by the correct integer ranges instead of using unbounded integers. Type safety is anything but easy, but it's obviously the right thing in visual dataflow. Type conversion by casting is unsafe and unnecessary. If you want to add a list of numbers, you have supply a list of numbers, or a list with untyped elements, and then the type system decides your list can only contain numbers. If you want to read numeric values from a list of strings and add them, you need to convert the strings to numbers first - a function call instead of a cast. Iteration and recursion are painful if your language doesn't have a good way of doing them. The approach I took was to have the simplest one (the feedback loop) as a base and see what can be developed naturally from that, instead of having a special loop construct. This is completely general, and there are examples throughout the tutorial. The lesson on macros, in which iteration is extracted away, hints at how to make iteration less painful. There's a good reason why I wrote an interpreter first: to iron out problems with function calls, functional arguments, iteration and recursion. All the data values are tagged. There are queues at each vertex input instead of a global stack. So the underlying machine is dataflow. If you like, you can give a simple example (in Lisp, C, Java, or Python) of something you think cannot be encoded in my language and I'll see what I can do.
- icebraining 9y agoYou 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. You should look into Idris; what you describe is just another type. http://docs.idris-lang.org/en/latest/tutorial/introduction.html http://docs.idris-lang.org/en/latest/tutorial/introduction.h...