Why is there no tradition of CLI and TUI apps?

,

I’ve been enjoying the explosion of new linux CLI and TUI apps written in “something better than C++”, often new and improved versions of existing unix utilities (e.g. ripgrep, exa, bat). The rust and go communities seem to have embraced this enthusiastically, and I see zig picking up steam too. I’ve always thought ocaml would be a great language for this sort of thing, and there are frameworks like minttea that would do a lot of the heavy lifting, but I haven’t actually seen much in the way of end-user applications.

Any thoughts as to why this is so? (I remember once joking that the ruby community put out a lot of libraries and very few applications; I wonder if ocaml is similar, where people write a lot open source libraries, and use ocaml to get work done, but there is not a strong tradition of open source end user applications.)

6 Likes

Generally to be “better”, you either need to be more performant than the original or solve some fundamental bug or UX issue the original application doesn’t have. It will be hard to beat old tools on performance, & in the case of exa & bat, I don’t know that they really improve the UX over ls & cat to warrant having them on the $PATH. With these sorts of coreutils clones, often it just feels like just ‘rewritten in Rust’ syndrome.

If you are talking TUIs however, there’s a lot more applications in need of help… ikhal written in Python has issues with perf, deps. Jackline in OCaml hasn’t been updated with modern features. There is no lazygit for Darcs or Pijul. Newsboat alternatives still don’t feel nicer to use. ActivityPub also enjoys new clients for Lemmy, Mastodon, etc. There’s been a small push to have AI chat on the TUI with none of the options being exceptional. …All ideas anyone is okay to ‘steal’ so I can use them :slight_smile:

1 Like

The simplest explanation would just be ecosystem size, as uninteresting as it sounds.

The other part is a lot of work done for CLI/TUI apps/lib is very time consuming but very mundane at a technical level, so you need persistent people who themselves want these apps to exist in order to have these apps. More established ecosystems alleviate this by having out of the box widgets that do these, but you still need persistent people to write these widgets.

I’m gonna use my experience as an example at the risk of sounding like rambling:

I spent a day trying to get key binding grid blinking to work, the gif below shows what the completed version looks like:
Screencast from 2024-05-12 16-37-03

It’s not just “user press x, you flip the background to light, you wait, then you flip it back to dark”, since:

  • The app got better things to do than to wait 140ms, so you cannot just wait.
    • (Finding out a good blink duration took another half an hour of trial and error and asking around.)
  • The user may want to press or do something else while the blink is occurring, so you still cannot just wait even if your app has nothing else to do.
    • If you neglect this, your app will feel very unresponsive and misses user inputs.
  • The user may hold down the key/tap multiple times instead of tapping once, so you cannot just “queue up” key presses. Since by the time you finish processing a key press and turn the background back to dark, the user may still be holding down the key and expects the background to be light instead of dark. This is followed by waiting for the queue to clear, so user’s next set of inputs are only registered after noticeable delay.

So what you ended needing is a monotonic clock (you have to anticipate hardware/OS clock updating) to timestamp when the background was set to light (lets call this timestamp t), and you only turn the background to dark at the t + 140ms mark if there has not been any updates since t.

But you don’t want your fiber run in a busy loop to check this, so you end up needing two fibers (technically, the first fiber can be rolled into a function to be called upon key press since the operations are quite minimal, but anyhow):

  • First fiber
    • Waits for key press
    • Records current time t under label in a mutex protected hash table
    • Turns the background to light
    • Generates secondary request of the form (t, t+140ms, label) to add to the queue for the second fiber
  • Second fiber
    • Waits for the secondary request (t_req, t_target, label)
    • Sleeps until t_target
    • Check if the time recorded in the hash table under label matches t_req, and only turns the background to dark if so

And only after all that, you finally get the tiny UX improvement you want. (In the context of my app, it was the best way to give user visual feedback, so I had to do it this way.)

2 Likes

I think the previous answers do a good job of addressing your question about CLI/TUI stuff specifically. But I wanted to address the broader claim you imply here:

IMO, this seems to overlook Ruby’s niche: applications written and delivered as web servers (many of which are OSS). When that is in scope, it seems to me that a large number of widely used applications have been written in Ruby. Similarly, if your definition of “end-user” is broader than “People who are not professional developers” – which I think it must be if you are including specialist CLI utilities like ripgrep – then I think it is fair to say that quite a few notable OSS applications have been written in OCaml. However, the principle niche on which these applications are focused is not CLI utilities (and especially not rewrites of existing utilities). Here are the notable applications that I can think of off hand:

  • the OCaml compiler
  • the dune build system
  • the opam package manager
  • the menhir parser generator
  • the ocamllex and sedlex parser generators
  • the js_of_ocaml transpiler
  • the oinstall, cross platform, decentralized package manager
  • the alt-ergo solver
  • the Frama C program analyzer
  • the Coq compiler and tooling
  • the F* compiler and tooling
  • all three implementations of Lambda Prolog
  • the catala law specification language
  • the Irmin database
  • the Mirage unikernal operating system
  • the mdx literate programming tool
  • the obuilder containerized building tool
  • the ocluster distributed work scheduler

I’ve used most (but not all) of these, and the ones I’ve used do have a CLI. Notably, in every case this is just one particular interface into using the application, rather than the singular interface to the the application, because none of these are primarily command line utilities for terminal foo. AFAICT, there is an evident specialization in the niche(s) occupied by these tools, which is connected to the roots of the ML language family and follows directly from programming language theory praxis.

All that said, I see no reason why OCaml can’t also be a great language language for command line utilities and TUI apps, for programmers who are primarily interested in those applications!

6 Likes

I agree, there is a lot of fiddly and tedious detail involved in writing a TUI (and technically in a GUI but the frameworks have solved most of them for you by now), but I would think that even that sort of thing would be nicer to do in ocaml than it would in C or C++, or even in rust.

But you’re right about the overall ecosystem being a lot smaller and therefore having less chance of having the few people who are interested enough in the problem to power through the tedious bits and write the frameworks.

I briefly toyed with writing a tig for mercurial in ocaml, but discovered getting at the underlying mercurial data for it from ocaml would be a pain.

1 Like

To be fair, this was back in the early 2000s when ruby didn’t have quite as much of a presence (though rails was already catching on) :slight_smile:

I do agree with your point about there being a lot of significant and high-quality end-user applications written in ocaml, particuarly in the area of language tooling; I was just wondering why the “rewrites of small CLI utilities” subculture took hold in the rust ecosystem but not the ocaml one, and why the current TUI renaissance seems to be using go and rust but again not ocaml. In terms of how pleasant it is to write these things it feels like ocaml should be right up there along with the other popular compile-to-native languages, especially since a TUI does not need the low-level control rust offers.

2 Likes

Is it curious there aren’t more CLI / TUI apps, someone should start writing some :smiley:

From my experience of writing (and not always finishing) TUIs in OCaml, for ocaml-ci/ocluster, a cucumber test runner, gitlab client and Mach-O binary editor. There was a lack of maintained libraries for working at a good abstraction level, I mainly used lwd which is an incremental style library with Nottui. It needs more users to shake out the bugs and missing functionality. There are other options but nothing that seemed finished.

Doing pretty printing to the terminal is ok but unsatisfying. I use fmt for doing that but I find the API for fmt doesn’t stay in my head and I need to search examples every time to work out how to use it. progress is a great progress bar library that is a pleasure to use. cmdliner for cli parsing only missing support for autocompletion, it is worth looking at climate for what autocomplete might look like.

8 Likes

Creating a CLI app in Go is pretty much a slam dunk since you can very easily cross compile static binaries to the 3 major platforms Linux, MacOS and Windows. With the standard tooling…

As a consequence, distribution is very easy, frictionless.

So that probably explains most of the appeal IMO.

5 Likes

As people are joining the community, these CLI tools are starting to appear, eg

And it seems natural that more will appear in the future.

11 Likes

There’s also GitHub - davesnx/query-json: Faster, simpler and more portable implementation of `jq` in Reason. And some javascript related projects like GitHub - fastpack/fastpack: Pack JS code fast & easy, or fnm which used to be written in ocaml I believe.

I don’t think it’s just about the size of the community. It’s also about who is part of the community. When reasonml started to pick some steam it was pretty clear to me. The people who joined ocaml through reasonml were more interested to this kind of app.

2 Likes

I discovered OCaml many years ago via the amazing Unison File Synchronizer. The quality, reliability and feature set of this CLI tool (which also has a GUI) was impressing and I wanted to know more about the programming language it was (and is) written in.

More than 15 years later, I still use Unison every day and OCaml is now my main programming language.

9 Likes

Bob is also a CLI tool to transfer documents between two peers. And it uses progress for the loading bar :slightly_smiling_face:

6 Likes

I will say that based on this low-level implementation-description - that there is a big untapped potential for reactive TUIs in OCaml, as we have great and elegant reactive libraries in OCaml. These both exist in impure and pure FP forms - where I much prefer the pure style of FRP. With these it’s straightforward to declaratively express the semantics that you describe.

My own personal choice for reactive and animated TUI apps has come to be a mix of the following libraries:

  • react_lwt : programming declaratively with time
  • notty: declarative TUI
  • fry: my own unpublished FRP-tools lib - where the musical concept of envelopes map directly to the semantics of triggering an animation in a UI. An envelope can be simply understood as a float signal (in the range [0;1]) that is triggered to move by an 'a event - this can then be mapped to control e.g. the colour of the UI element.

I don’t have a TUI like this opensourced right now, but you can see an example use of it here. You can find examples of using envelopes in the fry examples directory.


Edit: Forgot that fry has some simple TUI code here (:

5 Likes

I am using notty, nottui, lwd and eio, but never really tried the lwt tooling cause I need multithreading.

How does signal in fry work? I feel a general control signal primitive of sorts (which is what I’m interpreting) would have solved my issue indeed, but not obvious to me how to implement it without needing similar scheduling mechanisms in place.

1 Like

I havn’t mixed OCaml-5 domains and FRP yet - but because react is based on ephemerons, then my guess is that you would need to keep the FRP graphs of react disconnected between domains. An idea could be to have a primary domain running react_lwt and then schedule work on other domains. As I understand it, eio has support for scheduling lwt threads.

fry is a library that builds on top of react_lwt - where signals are an abstraction from functional reactive programming (FRP). Basically they are values that change over time that you can subscribe to using FRP combinators - e.g. a counter for how many times a user clicked. Otoh. events are streams of instantaneous events - e.g. a user clicks a button. There are more aspects to it, so you should read up on FRP if you are interested.

2 Likes

I once created a command-line bookmarking tool called Favemarks in OCaml using Janestreet’s Core Command.

1 Like

Since we speak about TUI here, it’s possible to put the fancymeter off-the-scale and be inspired by tools from Charm
Just look at those, aren’t they pretty?

And the framework itself: GitHub - charmbracelet/bubbletea: A powerful little TUI framework 🏗 (an original that inspired already mentioned MintTea). There is one step further: GitHub - charmbracelet/lipgloss: Style definitions for nice terminal layouts 👄

2 Likes

Just for the record, exa is dead, long live eza.
Speed of the the Rust ecosystem is astonishing, need to keep an eye on any changes at least weekly :wink:

1 Like

I’m a little late to get the party here but, I’m working on a TUI for the jujutsu version control system called jj_tui: GitHub - faldor20/jj_tui: A TUI for the Jujutsu version control system .
It’s based on nottui but includes quite a few improvements to nottui, a few changes to notty and a ton of custom components.

Over the coming weeks I’ll be releasing my fork of notty and nottui. I have a mostly done article about how nottui works and I’ll work on a tutorial when that’s done.

So I’d say, there should be TUIs made in ocaml and I hope to contribute to making that much more accessible.

5 Likes