The Janet Programming Language
My story with Janet
Since Advent of Code in 2020, I’ve been doing 95% of my hobby programming in a language called Janet, which not even I woud have predicted before that. I’d just broken my “heavily multithreaded or bust” phase by using Ruby for a few things, after having been big on Nim, which I gave up because of not feeling at home in the web-development frameworks it has. Both are fine languages in their own right, but where not where my brain wanted to be. Then, I thought I’d give Janet a try during AoC 2020. I’d tried lisps in the past. (And yes, despite what the Common Lisp community has to say, Janet is a lisp, even if it’s not a Lisp), but none of them ever quite stuck for me.
As I was going through the AoC challenges, parsing came up quite a lot, but Janet didn’t have handy Regex Bindings. Instead, it had Parsing Execution Grammars (PEGs). Okay, I thought, I’ll give these a shot. And I ended up actually being able to understand and use them, even if it was a little fiddly at times. A little personal history is in order. Between the years of 2011 and 2016ish, I’d done a lot of self-directed research investingating how to build a programming language, and one of the first steps that came to mind was how to parse things. I ended up reading about a lot of different parsing mechanisms, and PEGs were among them, but the examples I’d seen where hard to follow or understand. But, for some reason, Janet’s PEGs clicked for me. Some combination of maturing as a developer, Janet’s better presentation of the PEG concenpts, or how Janet’s PEG API overlaps with parser-combinators (something else I tried, and did semi-ok at) helped things gel in my head, and I fell in love with Janet.
Which is ironic, because since that AoC challenge, I’ve used PEGs all of maybe 4 times in 50+ projects I’ve started with Janet since. But, I’ve since discovered a lot of other things I like about Janet. The prevailing theme among all of these is that Janet is a collection slowly gathered clean choices. It’s not Innovative like Rust, Haskell, Pony, Scala, or Erlang. It’s a late-2010s lens applied to a mélange of Lua, Ruby/Python, Tcl and Clojure.
So, what do I like about Janet? I like the syntatic simplicity. I like the fact that you can “call” associative data structures
([0 1 2 3] 0) # -> 0, rather than wrapping that access in a
get call. I like the soup of macros and dynamic variables that make DSL construction possible, which makes encoding intent much more straightforward than in something like C# or Go. I like that
project.janet have first class support for building statically-linked executables and archive-libraries that mix Janet and Native modules into a single artifact, which makes building small CLI tools for Windows a breeze. I like that the Janet REPL has easy access to documentation attached to any given function/variable/module. I like that Janet makes writing C modules about as painless as that can be (some pain will remain, depending on the C librarires you’re trying to interface with). I like fibers as a collective tool for managing both asynchrony, and code “signals” (think having the ability to throw exceptions, but also use that for general messaging, as needed). I also like that Janet embraces a very clean flavor of multithreading that seems inspired by Tcl/Erlang, where memory isn’t shared, but one can send messages over channels.
All of this adds up into a language that I find very easy to write (I’ve written over 8kloc of Janet in my spare time in the past 9 months) when the libraries exist for a given task I’m doing. That does, however, bring me to some very important points about Janet.
I’ve been singing the praises of Janet for over 600 words, now it’s time to cover the rough edges. And there have been quite a few, though less due to the last year. The biggest rough edge of Janet is that the standard library is still getting bugs shaken out of it, especially on Windows, which happens to be my main development platform. In the last 9 months, I’ve found a 5 bugs in the standard library, ranging from a bug in multi-char string split, to subprocess code not actually killing child processes, to async file writing on Windows not actually writing anything other than the last line to a given file. The good thing here is that once a proper reproduction is in place, usually the fix hasn’t been terribly hard. @bakpakin, the creator/maintainer of Janet is responsive, and the bugs I’ve found, either he’s fixed in the next 48 hours, or has merged a PR containing my fix, if I’ve been able to fix them.
The other issue that has taken more of my time over the past 9 months is the fact that Janet’s ecosystem still has a lot of gaps compared to even other semi-niche langauges like Elixir or Lua. This is why I’d describe Janet as a language for hobbyists and enthusiasts, folks who don’t mind popping the hood on how things work. Because, with Janet, you’re given a language that is definitely amenable to popping the hood, but you also end up having to build more than a few of the things that would be considered “under the hood” in langauges like Ruby or PHP.
What I’ve done with it
Speaking of things built in Janet, I figured I’d end with a list of things I’ve built or helped with in Janet over the last 9 months, as a sort of overview of the sort of work being done with it. In no particular order: A less optimized version of entr called eye, which works on Windows, but does poll files, rather than rely on IOCP, which has changed how I approach development in a lot of ways. I’ve also helped add source mapping to C functions in the Janet stdlib (though I mostly just got the ball rolling there, @bakpakin set up the relevant macros, and @sogaiu did most of the grunt work). I written a dozen small CLI tools, including a program for measuring the size of stdin, to programs for annodating the current time onto stdin, to a task tracking program, to a program for rendering local SVN diffs in a web browser, to a program for getting/setting the dimensions of the current screen on Windows. I’ve also written a few libraries for Janet, praxis for dealing with data schemata, stringx to add make string manipulation less painful, err, of which I mostly use
(err/str "Message with " data " here"), but which I use often. I also helped powered by Janet get started, and was able to help it get a better project.janet parser, to better extract metadata. And more.
All that to say, that Janet has a lot of oppurtunities for buiding cool things, and learning how to grow a programming language ecosystem, but that the ecosystem is far from fully formed yet. So, if you want to join a group of enthusiastic hobbyists, Janet has a great community. If you just want to build the next feature, you’d be best to look elsewhere.