3 ms·
Interesting, and clearly a lot of work's gone into this (60,000 lines of Typescript), particularly the UI, which is impressive (if, sometimes, over the top).
by DonaldFisk 3y ago
Interesting, and clearly a lot of work's gone into this (60,000 lines of Typescript), particularly the UI, which is impressive (if, sometimes, over the top). I've been developing a similar system (http://www.fmjlang.co.uk/fmj/tutorials/TOC.html http://www.fmjlang.co.uk/fmj/tutorials/TOC.html) and it's interesting to note the similarities and differences.
Similarities: code as directed graphs (less obvious in FMJ); can only connect outputs to units of compatible type; if and wait (looping is handled differently); sticky values; sliders. These design decisions are practically forced on you, but are often absent in earlier visual dataflow languages (e.g. Prograph, LabVIEW).
Differences: (1) inputs are named in Unit, ordered in FMJ (though they're named in formulas and edges can be labelled). (2) I experimented with automatic code layout but found this was too slow and not always what I wanted. Well done for getting this to work. (3) FMJ is now fully homoiconic - this maybe isn't a priority for Unit.
The Unit design philosophy is explained in https://github.com/samuelmtimbo/unit/blob/main/src/docs/concept/README.md https://github.com/samuelmtimbo/unit/blob/main/src/docs/conc... . This doesn't mention earlier approaches (e.g. the Manchester Dataflow Computer, Prograph) and it seems to be based on vaguely similar ideas developed more recently (Morrison's Flow Based programming; possibly React and similar systems for web development - I'm unfamiliar with these).
I have a number of questions:
(1) How does the type system work? Is it Dependently typed, Hindley-Milner, or something more basic? (FMJ is Hindley-Milner, with dependent typing partially implemented). How are new types be defined?
(2) How is the visual representation stored? One criticism I faced was that people wanted a readable textual representation which would work well with existing version control systems, a problem I have now largely solved.
(3) How are runtime errors handled?
(4) Is recursion supported? (I assume yes, but I didn't see any examples.) What about macros?
(5) What does Unit compile to? (FMJ has an experimental compiler where programs are compiled by running their source without evaluating their inputs, output is Lisp.)
- jsunderland323 3y ago> 2) I’ve thought about this problem a lot. Like to a very unhealthy degree… To the extent that I’ve been working on a visual dvcs full time for over a year, that interops visual programming tools with textual codebases that use textual version control. When you say you solved this, is it because you expect users of your tool to be able to understand and reason about however you’re storing your state to disk? Xcode does this with the infamous project.pbxproj and it still causes immense merge conflict pain to this very day (and it’s one file), on the other end of the spectrum, large game studios have basically forgone dvcs to use shader editors at scale. If your language tool is simple enough to reason about textually, doesn’t that limit the utility of your language? I don’t mean in the sense that you make it deliberately hard to reason about your ast equivalent but presumably it wouldn’t take much complexity before a bad merge conflict could break relations and make malformed states. How do you handle that without writing a hand rolled merge driver for each vcs you support? In my humble opinion this is kind of the breaking point of almost all higher order programming tools. I think it’s also the reason most “no-code” or “design-to-code” tools feel like a sham to engineers. I’d love to hear how you overcame merge conflicts for your tool, I’m skeptical, but I’m all ears.
- DonaldFisk 3y agoIt's low level, but still readable and, if necessary, editable. The problem I had was converting my visual code into a topologically sorted directed acyclic graph, with just vertices and arcs, where the arcs into and out of vertices are unordered. I found a way of doing this (thereby getting homoiconicity for free) and now can just store the textual representation of the graph, with each vertex on a separate line. I'm in the process of replacing the earlier representation, where I just stored lists of objects in an ad-hoc fashion. This works for pure dataflow programs or anything else which is equivalent to a DAG, but would not for e.g. LabVIEW.
- jsunderland323 3y agoThat’s interesting to hear and I think I arrived at a similar homiconic IR state for my tool too. I think there are some problems you might run into if you’re doing any sort of relational cascading but this sounds sane and like a good approach. Thank you for sharing!
- samuelmtimbo 3y agoHey, thanks for the analysis and questions! I did check on FMJ many years ago when researching about Visual Programming Languages. I recall the very inspiring notes on the FMJ website (http://www.fmjlang.co.uk/fmj/FMJ.html http://www.fmjlang.co.uk/fmj/FMJ.html). (1) The type system is inspired by TypeScript. It has generics that are resolved when unit pins are connected to concrete types. Right now there’s no way to define new types. Hopes are that type inference alone would be good enough for a start. Likewise, new interfaces cannot be created. Only the system defined interfaces can be exposed, such as channel `CH` and media stream `ST`. That is interesting because it influences the design of machines in terms of common interfaces and protocols for inputs and outputs. (2) The visual representation is a graph specification stored in JSON. I think JSON works well with current version control systems in terms of diff readability, but I don’t see why custom unit components couldn’t be created to show the diff in graph, visually. (3) The unit will stop the data flow and emit an “error” event. To catch this event particularly, there’s a `catch unit. (4) Yes, there’s a few examples of recursive units, such as a `merge sort` in the open source system. The `editor` unit can’t create them from scratch right now though… but it can be done with a meta trick… (5) The graph is loaded and saved as a JSON. The JSON has to be read by an interpreter that will instantiate all the primitives and make all the connections. Unit specification is language agnostic, but currently the project is all written in JavaScript (works with Web and NodeJS).