Interactive programming

In the last two days, I wrote a lua module to parse a data format. It’s called ndb.lua and while it’s not yet production ready it’s only a few polishings away from that.

The “2 days” in the last paragraph isn’t a mistype, and I’m not lying either. It only took me about 2 days to get from the initial commit to adding the last public api function. It doesn’t feel much in retrospect, but before I started I thought it was going to take much longer, I wasn’t even sure I was going to finish it at all.

What certainly helped it is what I used to implement the library, the programming language lua and a parsing library lpeg. Specifically: it was their a bility to be used and explored interactively

When you run lua without any arguments, you get a repl, a place where you can type out lua code and see what it results to. You can do anything there, including loading your own code and trying it out. This is an example session:

$ lua
`Lua 5.4.7  Copyright (C) 1994-2024 Lua.org, PUC-Rio
> os.time()
1722352848
> os.date(os.time())
1722353027
> t =os.date("*t", os.time())
> t.day
30
> t.month
7
> t.year
2024
> t.isdst
true
> ndb = require "ndb"
> r = ndb.readstring[[this is=a test="$HOME"]]
> r.test
/home/readmemyrights
> ^D
$

This kind of usage is common before actually coding, to get yourself used to the language or one of its libraries. It can be however quite useful while developing. While making ndb.lua I had “private but exported” functions that I used to test some of the parts within the library itself. For example, I’d make a function called ndb.Dqword to test my pattern for double quoted string. Here’s an example of that:

> ndb = require "ndb"
> ndb.Dqword("hi") -- no quotes, shouldn't match
nil
> ndb.Dqword[["hello"]]
hello
> ndb.Dqword[["Hello\x20there"]] -- should match
nil
> -- Uh? Go to the file, change things, and try again
> ndb = dofile "ndb.lua" -- force reload the module
> ndb.Dqword[["Hello\x20there"]]
Hello there
> -- works great!

While I was developing I got instant feedback of how my code work, and with the help of the history I could try the same input again with a new, hopefully fixed version of the function. Someone is likely to point how what I just described is just manual test-driven development. While I certainly agree that tests are useful and plan on writing some for ndb.lua there are good reasons to put off writing them and instead use the repl at the beginning:

To put it simply, interactive development is fast. And it’s not important just for speed’s sake, if something takes a while people lose interest. While making ndb.lua I didn’t have to wait long to reload my library, run tests, or anything else. I never had a chance to wait for something doing nothing and losing track of what I wanted to do.

That’s probably the main reason why I don’t program in C much any more, or static compiled languages in general. I always first have to develop my program to a point when I can test it interactively. And even then I miss just being able to call functions from a repl. It doesn’t feel like I’m achieving much until it’s finally completely finished, which can take a while.

How to do this?

Many text editors have plugins or extensions to make working with repls easier. Personally however, I prefer to keep it simple.

I use tmux to have multiple terminal windows, and when I’m working on some code I usually have a small repl at the top of the screen. I can switch between it and my editor with prefix-o. I don’t often find myself directly sending text from my editor, but if you do one of those extensions would definitely be useful.

However, the best part is that you don’t have to. You can open a separate terminal program, or even close the editor, go into the repl, leave it again and so on. As long as you can access both of those, you’re set.