Valeri: a pure Lisp with algebraic effects
Find a file
2024-09-11 23:25:17 +01:00
src Report errors with syntactic context in the compiler 2024-09-11 23:25:17 +01:00
test Compile syntax expressions 2024-09-09 22:56:06 +01:00
.clang-format Initial commit - add basic boilerplate for POD types 2024-07-19 02:29:30 +01:00
.clangd Initial commit - add basic boilerplate for POD types 2024-07-19 02:29:30 +01:00
.gitignore Initial commit - add basic boilerplate for POD types 2024-07-19 02:29:30 +01:00
CMakeLists.txt Add simple and naive string building functionality 2024-09-08 00:52:20 +01:00
example.vli Implement basic stdlib and a simple "println" function callable from lisp 2024-08-25 00:55:11 +01:00
flake.lock Initial commit - add basic boilerplate for POD types 2024-07-19 02:29:30 +01:00
flake.nix Initial commit - add basic boilerplate for POD types 2024-07-19 02:29:30 +01:00
LICENSE Initial commit - add basic boilerplate for POD types 2024-07-19 02:29:30 +01:00
lisp.nix Initial commit - add basic boilerplate for POD types 2024-07-19 02:29:30 +01:00
README.md Allow creating arrays from code 2024-09-01 16:13:28 +01:00

Experimental Lisp compiler

This is a Lisp compiler, that I'm writing to test a few ideas and try to make a language that I would want to use for personal projects.

Note: project is in very early stage.

Features that I would like to see (in no particular order):

  • small and simple core with focus on correctness/security
  • Erlang-style concurrency and message-passing instead of threading/async-await
  • per-actor garbage collectors
  • native support for immutable data structures
  • fast startup
  • 64-bit only
  • ability to produce small redistributable binaries
  • persistence primitives out-of-the-box

Rationale

I find that writing something simple in popular programming languages takes an ungodly amount of time. Mostly because those languages try to cater to a very wide audience. If I have a need to glue a couple systems together, I need to pull at least a dozen dependencies, and keep them up to date all the time.

Case in point: one day I needed a simple script that would crawl a github organization, collect information from there about open pull requests, create a dashboard that presents the information in the way I want, and occasionally pings people on open issues and pull requests.

Sounds simple, right? Well, not so fast. You cannot crawl GitHub all the time, because it has request limits. So you need to run a daemon in background, and update the currently synchronized state. Oh, so there is state now? How about you store it in a database? And then add migrations to initialize the schema?

Don't want to mess with databases? Then you can go the route of just-put-everything-into-a-big-json-file. Except then you have to turn dates into strings, because JSON doesn't allow custom types. And also, no integer keys in dictionaries for you. And tuples now turn into arrays, so when you read them back, the code will treat them differently (because surprise-surprise, arrays cannot be dictionary keys, while tuples can).

Frankly, not everything needs a scalable persistence system capable of handling hundreds of thousands of users. But I would prefer the language to provide something decent that I can just put my data structures to, and get them back exactly the way I put them.

Then, the second problem with this particular case is that there are multiple parallel things happening. At least, one of them is polling the API, and the other sends notifications once a day, and the third updates the dashboard every now and then. Doing this in Python requires either threads, or async, or a hand-rolled event loop with a timer.

For a lot of small apps, I don't care about copying data multiple times. But what I do care about is never having to deal with data races or inconsistencies. So, in my opinion the Erlang model where actors are isolated and exchange messages wins hands down. No "function coloring", no shared state, relative ease of debugging, etc.

I would even go as far as to say that many of the production apps I worked on in my career could have easily been done without any shared state.

So, a language that can take care of such basics and get out of your way would be very nice. Even if it would be as slow as TCL, it would still be fine for many practical things.

Compiling and running

You'd need gcc and cmake.

At this moment, only a bare minimum of functionality is implemented.

To run:

cmake .
make
./valeri example.vli

To run tests:

ctest

Influences

  • TCL
  • Clojure
  • Erlang

Get in touch

I'm working on this alone at the moment, and don't expect it to turn into anything. But if you like the idea, you can reach out to me on Matrix: @knazarov:knazarov.com.

Acknowledgements

The name of the language is the name of my love, Valeri.