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

36 lines
1.7 KiB
Markdown
Raw Permalink Normal View History

2024-08-26 22:30:59 +00:00
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](https://git.knazarov.com/knazarov/valeri) without any arguments,
2024-08-26 22:30:59 +00:00
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](https://github.com/antirez/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.