In my graphical programming project (which I’m really hoping to open source by the end of the month), the programmer uses Racket. Using a Lisp for graphical programming makes a lot of sense, and I’ll try to explain why here. To do so, I’ll have to go off on a short tangent about what are the opportunities for improvement that graphical programming offers, and then I’ll show why Lisp is ideal as a language to be edited in this manner.
Separation of Entry and View
A critical aspect of this is the decoupling of the method of editing code from the manner of viewing code. It is useful to add visual structure to code, so we’ve evolved standard indentation patterns. However, it’s frustrating and time-consuming to manually indent code, so we built editors to do it automatically. This separates the act of typing in the code from the way it’s presented, and that’s a good thing.
I contend that this principle can and should be extended much further. We should greatly increase the ratio of information added to number of key strokes. Why do I think this is possible? When I look at code, my understanding of the code is much more in-depth than what I see. There is much more structure to the code than is easily visible. Since there is that much structure, the editor should display as much of that structure as possible. Indentation and syntax highlighting is good, but there’s much more we could do.
The question is, then, what can we we do to improve the experience by programming graphically? Obviously, because of the tree-like structure of Lisp code, we can simply visualize the tree in various ways. Most likely, though, instead of simply displaying a tree with no understanding of the symbols, the program should have an understanding of a function like map and display it in a particular way. But how? What would give a better intuitive understanding of the map function than simply the word map? I propose simply showing more or less what a map actually is: simply show the function applied to several of the elements. Thus, instead of this:
(map f l)
We have something more like this:
(f (list-ref l 1)) (f (list-ref l 2)) (f (list-ref l 3)) ... (f (list-ref l (length l)))
To me, this is significantly more intuitive. I’m very comfortable using map because I’ve used it so often for so long, but I still understand the second faster on first glance. This is the same reason why the ellipses notation is often used in math when first explaining a sequence. Only after you understand the sequence do you write it in sequence notation. Humans are very good at pattern matching, so when looking at the second example, it is very obvious what exactly is happening from one line to the next.
The main problem with the first example is not that it’s hard to understand. It’s easy to understand – the problem is that it requires being understood at all. It requires you to mentally imagine the second example without seeing it. We have these powerful machines right in front of us that are already going to create the second example anyway when it runs the code, so why must we duplicate the computer’s work and imagine it in our mind? Why not just have the computer show you what it’s thinking?
There are two main reasons why we don’t do the second example when we’re writing code textually. First, it’s more verbose so it takes more time to type. Second, it’s hard to maintain and modify – one must edit each line to make a change. Having the computer generate the second example from the first example seems like the best solution. That way, we must only tell the program “apply f to every element in l”, and it creates the second example. If we want to change something in it, we should be able to change it on one line and it will be reflected on every other line.
This may remind you of spreadsheet software. I think that’s a good thing – it’s easy to see what’s happening in a spreadsheet because all the data and patterns are easily recognizable. To change a formula in a spreadsheet, you just need to change it in one cell and then tell it to put an analogous formula in the entire column. This is a very intuitive and easy way to model computation.
I should note that spreadsheet software is only good for particular types of programming, and its model falls apart very quickly for general programming. The last thing I want to do is bring Lisp down to the level of a spreadsheet. That’s why a graphical programming editor should understand map and display it in this way, and it should understand fold and display it in a similar way, but it should understand something like a struct definition or a conditional or a macro in a very different way, and it should display it in a way that’s specifically tailored for that operation.
Incidentally, for fold, I’d imagine it displaying this:
(foldl f i l)
As something like this:
(set! res (f (list-ref l 1) i)) (set! res (f (list-ref l 2) res)) (set! res (f (list-ref l 3) res)) (set! res (f (list-ref l 4) res)) ... (set! res (f (list-ref l (length l)) res))
Obviously, when you’ve “zoomed out” on your code, this should collapse into the more concise version.
A Lisp file is essentially a bare AST. Thus, the structure of the program is very clear to the programmer. This is critical for graphical programming since the major advantage of graphical programming is that it can be used to very clearly expose the structure of the program. This allows the programmer to more naturally reason about the logic involved.
Creating a program to graphically display and edit an AST is not particularly hard – it’s simply a question of visualizing and editing a tree. There are many different ways to accomplish that, but a minimal functional program can be created fairly simply. This, however, does not likely give a compelling reason to change away from textual programming – the additional clarity gained by the tree visualization is likely negated by the additional difficulty in editing the tree.
Thus, we add more understanding to the program. This is easy to do with Lisp since there is basically no syntax in the code. Thus, all of the “syntax” is controlled by the editor. Note that for a graphical editor, there is a difference between the syntax of typing code and the syntax of displaying the code. Behind the scenes, though, it should simply be an AST.
I realize that Lisp has more features than merely its homoiconicity. However, the rest of the feature are intimately related to its homoiconicity. For example, macros are awesome because they’re easy to think about – you just need to manipulate a tree. The ease of quickly abstracting and the functional nature of Lisp are both also very natural once you have homoiconicity. A graphical programming editor should be able to support these other features very easily. I haven’t talked about these features simply because there will be very little change in them from textual to graphical programming.
Eventually, I believe that languages will be created specifically for graphical programming editors. Right now, it’s important for languages to be backwards-compatible with textual editors, at least in a pinch. This allows you to use not only vim but also grep, sed, diff, and so forth. Until we have good enough graphical tools to replace these, the various Lisps are the best candidates for graphical programming.