knazarov.com/content/posts/valeri_now_has_a_repl/note.md

1.7 KiB

X-Date: 2024-08-26T22:05:23Z X-Note-Id: 8bb59a7f-ed5e-4407-b703-d592746e5ca8 Subject: Valeri now has a REPL X-Slug: valeri_now_has_a_repl

If you run Valeri without any arguments, it now drops you into a REPL, where you can type something like this:

$ ./vli
vli> (println "sum:" (+ 1 2 3))
sum: 6

I've hand-rolled most of the terminal-interaction functionality based on the linenoise library. I didn't copy the code verbatim, because I'm writing in C++ and overall want a slightly different editing experience than what linenoise provides.

However, since the linenoise codebase is very compact (around 1k lines give or take), it is great as a learning material. By just studying the code, you can easily understand how a terminal raw mode works and how command prompts deal with escape sequences.

With the current REPL, you can already do quite a bit. But it has one flaw: there is no shared state between evaluations. So you can define a function in one REPL line, but you won't be able to call it in the next REPL line. This is because I currently don't have a notion of "global state", and compilation phases of different REPL executions don't share any module context between each other.

Having mutable module state is easy in most dynamic languages. But since Valeri is "purely functional", it poses a problem. I initially didn't think about having any sort of per-module global variables except constans. And if we treat REPL as a way to incrementally add something to the module, then the convenience of just playing with data/variables is lost.

I probably should go and study prior art on how purely functional REPLs are made.