DEV Community

Mike Skoe
Mike Skoe

Posted on

I've done TUI (Terminal User Interface) app. This is what it looks like

app workflow

For the last few months, I was learning OCaml in my free time. To improve the skills I decided to make a little minimalist app, for listing all things you have and reasoning about their necessities. The idea is to make an app with a terminal user interface using sqlite3 as persistent storage. Here is the link to GitHub

In this post, I would like to shallowly sketch the code and the main architecture.

Since the app not performs any heavy computations and has no timers - the main loop will be synchronous, which simplifies implementations quite a bit.

let rec loop (state, msgs) =
      let open State in
      match msgs with
      | [`Navigation Navigation.Quit] -> ()
      | msgs ->
      (state, msgs)
            |> MyState.reducer
            |> Ui.reducer
            |> loop
Enter fullscreen mode Exit fullscreen mode

main loop

As we can see, the main loop is implemented as a recursive function that takes a tuple of state and message list, modifies the tuple until any quit message, and return the unit as a result, to stop execution. The message is represented as a list because it allows batch actions.

To see how the tuple is changing, info is drawing and an event is handling we should zoom into the loop a bit

main loop zoomed

UI - is the module responsible for, you know, UI and event handling. It takes our tuple and returns a tuple with modified messages.
Reducer (in code it is called State, but here I’ve changed the name to prevent name collisions) - is the module responsible for changing state, depending on messages.

Let's again zoom into the modules, but first, familiarize ourselves a bit better with state and message.

state and message

Since the app has only two "pages", it is represented as a variant with two options. Every page has its own variant of messages and its own logic to modify the state via the messages. The only "high level" message type is navigation, which allows switching between pages. The main message type has a shape of polymorphic variant because this way there is no cyclic dependency.

ui and reducer

As some can guess, the architecture was heavily inspired by Elm and Elmish parent-child patterns.
Notty - is a cool library, which is a perfect choice for a simple terminal draw. But keep in mind, that you will be responsible for focus management and isolation of all event handlings.
Sqlite3-OCaml - is totally imperative library, because it is just bindings on C library. But you get all the essential functionality with which you can build yourself the needed level of abstraction.

I have an assumption, that functional programming is a perfect tool for safe and reliable GUI and I always wanted to make TUI, so I believe, with this project and the tools I have achieved what I wanted.

If you like to get a more in-depth overview of OCaml/code/tools, let me know.

Discussion (0)