On writing interactive UIs with straight-line code.

The first several programs I wrote were command-line apps that used C#'s Console.ReadLine method to get input from the user. It was simple, blocking, direct, straight-line code; whenever I wanted something from the user, and asked for it and waited until they replied. There are a lot convenient things about this style of user interface.

Later, when I learned to build graphical programs and web apps, I transitioned to using event handlers that would modify some data that would then get rendered on the page. It was tricky, asynchronous, callback-heavy, and scattered code; whenever I wanted something from the user, I gave them a button to click, added a click listener to update some state, and then read that state in the place where I actually wanted it.

The main pain point of this later approach that I wish I could get away from is that all state must be explicit. I'm not referring to the state the gets displayed to and modified by the user; that always needs to be explicit. What I'm talking about is the state of some interaction flow. It's the progress that the user has made through some function, and the information about which variables are available for computation. In the first approach, this state was kept implicitly by the programming language's scope, and the runtime's program counter.

An analog to this dichotomy exists in other fields of programming. In parsing, recursive descent parsers are direct and easy to write by hand, while automata parsers are indirect and so annoying to write that we rely on tools like Yacc to generate them. Another example is the recent trend for programming languages to support async and await keywords to facilitate clean asynchronous code; before these features existed, programmers had to choose between "callback purgatory" and making the state of the control flow totally explicit.

Here is an idea for how to make UI programming more convenient: enrich the kind of requests that Console.ReadLine can make. Instead of asking for plain text, the function should present a rendered snippet of HTML and wait for the user to interact with it. The function would return the specific interaction performed by the user, and then the rest of the code can branch on it and perform computations until it eventually calls Console.ReadLine again.

The result of this ida is that you write programs whose execution steps through various UI states. The user, then, is the agent that determines which branches of execution to take.

The mention of Console.ReadLine is purely for concreteness. In reality, you might have some monadic library with a present operation. Application code would be written inside this monad, and there would be a separate eval function that drives the UI forward as guided by the user.