A Scheme Tui Library Core

Inspired by a reddit post and the referenced blog post I have started a small library in R6RS scheme for the creation of terminal user interfaces and published it on GitHub.

The design is quite straight forward: creating such a user interface is the same as printing a list of characters to the screen. Some of these characters are ANSI escape codes and will be interpreted by the terminal and affect later characters. With that the function to draw is as simple as:

(define (draw list) (for-each write-char list))

The second design element is that all escape sequences are generated via macros at compile time. So far, I had only written a single macro, so this really gave me ample time to get better at using scheme macros. I stuck to syntax-rules macros instead of syntax-case because they allowed me to do everything I needed so far. Also I learned the following things about macros:

  1. They allow to define novel syntax (which is their main raison d’ĂȘtre). So I can now write (style bold invert "my text") and it gets translated to
(#\esc #\[ #\1 #\; #\7 #\m #\m #\y #\space #\t #\e #\x #\t
  #\esc #\[ #\0 #\m)

(a list starting with the escape codes for bold and inverted video, then the character sequence for the string “my text” and I cleanup all styles with the escape sequence to set the style to plain)

  1. They allow some things that a procedure cannot, because they have access to the syntax. So I started with a procedure to repeat a list of characters n times,e.g. like so:
(define (repeat n lst) (if (<= n 0) '() (append lst (repeat (- n 1) lst))))

Then I realised, that if lst contains escape codes for color or style, I would repeat these codes along with the rest of the list, even though their repetition has no effect on the outcome. So I turned repeat into a macro which allows the following optimisation:

(define-syntax repeat (syntax-rules (style)
  ((_ n (style e1 ... e2)) (style e1 ... (repeat n e2)))
  ((_ n list) !!!))) ;expand to the definition above, elided for clarity

This way if an expression is constructed as such (repeat 3 (style bold invert "my text")) it would repeat the character list for “my text” 3 times and then prepend the ANSI escape codes for bold and inverted video. Of course this macro will not be able to optimize something like this because it does not match the syntax pattern:

(define my-text (style bold invert "my text"))
(repeat 3 my-text)

Still it was a great insight for me into the potential uses for macros. Beforehand I always preferred procedures and did not see the interest in macros apart from saving typing by turning repetitive patterns or boilerplate into a macro.

There is still much to be done to turn this library into a complete TUI library. I did implement a way to draw boxes using different unicode characters. I hope to get back to it in the future, perhaps when I implement a terminal based game like in my last game jam participation. There I used an SDL based library which I found quite difficult to use and a lot of overhead just to draw some strings on a screen.

By the way, yesterday the Spring Lisp Jam 2023 kicked off. I will not have time to submit anything for this edition. However, it would be great if somebody decides to build something based on these foundation.