Why Janet?

yacin1 pts0 comments

Why Janet?

psst, hey kid, wanna read a weird programming book

April 12, 2023

Why Janet?

I never thought it could happen to me. I mean, parentheses? In this day and age? But for the past couple years, my go-to programming language for fun side projects has been a little Lisp dialect called Janet.

(print "hey janet")<br>I like Janet so much that I wrote an entire book about it, and put it on The Internet for free, in the hopes of attracting more Janetors to the language.

I think you should read it, but I know that you don&rsquo;t believe me, so I&rsquo;m going to try to convince you. Here&rsquo;s my attempt at a sales pitch: here is why you – you of all people – should give Janet a chance.

Janet is simple

Janet is an imperative language with first-class functions, a single namespace for identifiers, and lexical block scoping. The core of the language is very small, consisting of only eight instructions: do, def, var, set, if, while, break, fn. But thanks to macros, there are lots of high-level wrappers that give you more powerful or convenient control flow.

There are actually five more instructions that exist to support macros: quote, unquote, quasiquote, splice, and upscope. But you don&rsquo;t have to write those in &ldquo;regular&rdquo; code.

You can &ldquo;learn&rdquo; Janet in an afternoon, because the runtime semantics are extremely familiar: think JavaScript, plus value types, minus all the wats. And the rest of the language is small: the entire standard library fits on one page. It was this ease of getting started that got me hooked in the first place.

Janet is distributable

It&rsquo;s easy to compile Janet programs into native executables that statically link the Janet runtime. And you can share those programs with other people, without asking them to install Janet first – or your project&rsquo;s dependencies, or anything else for that matter. You don&rsquo;t even have to tell them it&rsquo;s written in Janet!

The way that Janet pulls this off is very elegant: Janet compiles itself to bytecode, and then writes that bytecode into a .c file that also starts up the Janet runtime. Then it compiles that C file with your system&rsquo;s C compiler. Since Janet is designed to be easy to embed, this makes perfect sense: it is, essentially, embedding itself into a trivial C executable.

A simple Janet &ldquo;hello world&rdquo; compiled to a native binary weighs under a megabyte (784K for Janet 1.27.0 on aarch64 macOS, but your mileage may vary). This includes the full Janet runtime, garbage collector, and even the bytecode compiler – so you can write programs that evaluate Janet code at runtime, if you want to.

This makes Janet an excellent choice for writing little command-line apps. Which is especially true when you consider that&mldr;

Janet is unrealistically good at parsing text

Instead of regular expressions, Janet&rsquo;s text wrangling is based around parsing expression grammars. Parsing expression grammars are simpler, more powerful, and more predictable than regular expressions. They aren&rsquo;t line-oriented, so they can parse multi-line text without a problem. They can also parse HTML, or JSON, or any other non-regular language. They can also parse binary file formats – they have no problems with arbitrary null bytes.

They really are parsers: structured, composable, first-class parsers. And they&rsquo;re pretty easy to learn!

Janet has the best subprocess DSL of any high-level language

There is a third-party library called sh that provides a shell scripting DSL that allows you to express pipes and redirects directly in Janet. Like this:

($ find . -name *.janet | say)<br>It&rsquo;s pretty incredible. It&rsquo;s such a nice DSL that I dedicated a whole chapter of Janet for Mortals to it – and the things that you can do with it. It elevates Janet from a reasonable alternative to Perl to a reasonable alternative to Bash for a surprisingly large range of programs.

Janet is embeddable

Lua has become the de facto &ldquo;embedded language,&rdquo; which is a shame, because&mldr; well, this isn&rsquo;t a post about Lua. You might not care about this very much, but there&rsquo;s a chance that it&rsquo;s just because you haven&rsquo;t tried it yet: being able to write progams that expose scripting interfaces is a pretty fun superpower.

Embedding Janet is very easy: the Janet runtime is a small C library, and all you have to do is link it in and then call regular C functions to manipulate Janet values. You can even embed it into websites, and write static sites with custom programmable DSLs!

Janet has mutable and immutable collections

Janet&rsquo;s collection types come in mutable and immutable flavors. Immutable collections have value semantics: the immutable vector [1 2] is indistinguishable from (take 2 [1 2 3]), despite the fact that they have different memory addresses. Mutable collections, on the other hand, have reference semantics: the hash table @{:x 1 :y 2} is only equal to...

janet rsquo language runtime regular first

Related Articles