Janet Programming Language: A Modern Lisp for Scripting and Embedding

Janet is a modern Lisp dialect that prioritizes simplicity, embeddability, and ease of distribution. It is designed to be an imperative language with first-class functions, lexical block scoping, and a small core consisting of only eight primary instructions: do, def, var, set, if, while, break, and fn.

Native Distribution and Binary Compilation

Janet allows developers to compile programs into native executables that statically link the Janet runtime. This process is achieved by compiling Janet code to bytecode, writing that bytecode into a .c file that initializes the Janet runtime, and then compiling that C file with a system C compiler.

Key benefits of this distribution model include:

  • Zero-dependency binaries: Programs can be shared without requiring the end-user to install the Janet runtime or project dependencies.
  • Small footprint: A simple "hello world" binary (Janet 1.27.0 on aarch64 macOS) weighs under one megabyte (approximately 784K), including the garbage collector and bytecode compiler.
  • Runtime evaluation: Because the bytecode compiler is included in the binary, programs can evaluate Janet code at runtime.

Advanced Text Parsing with PEGs

Janet replaces traditional regular expressions with Parsing Expression Grammars (PEGs). PEGs are structured, composable, first-class parsers that offer several advantages over regex:

  • Predictability: They are more predictable and less line-oriented, allowing for seamless multi-line text parsing.
  • Versatility: PEGs can parse non-regular languages such as HTML and JSON, as well as binary file formats containing arbitrary null bytes.

While some developers argue that PEG alternatives are not commutative, which can complicate debugging, they remain a powerful alternative to standard regular expressions for complex text wrangling.

Embedding and Scripting Interfaces

Janet is designed to be a small C library, making it a viable alternative to Lua for embedding scripting interfaces into larger applications. Linking the Janet runtime into a C project allows developers to manipulate Janet values using regular C functions.

Beyond traditional software embedding, Janet can be embedded into websites to create static sites with custom programmable DSLs. Additionally, the language includes built-in networking capabilities, a feature often missing in other tiny interpreters.

Metaprogramming and Compile-Time Execution

Janet provides powerful metaprogramming capabilities through macros and a unique approach to compile-time state preservation.

Macros

Janet macros allow developers to write code that generates other code by manipulating abstract syntax trees (ASTs). To maintain referential transparency, Janet allows the unquoting of literal functions within macros.

Compile-Time to Run-Time Value Passing

One of Janet's most distinct features is the ability to serialize the program's state to disk during compilation. When a program is compiled, Janet executes all top-level instructions and takes a full snapshot of the state. This snapshot preserves:

  • Shared references: Mutable values remain mutable after the snapshot is resumed.
  • Generators: Generators remember their exact instruction state.
  • Closures: Closures are preserved across the compile-time/run-time boundary.

This allows for high-performance optimizations, such as pre-calculating complex data (e.g., reticulating splines) or embedding assets by reading files at compile time.

Language Design and Syntax

Janet deviates from Lisp traditions to favor modern developer comfort:

  • Naming Conventions: It replaces legacy Lisp terms; for example, CAR is first, PROGN is do, LAMBDA is fn, and SETQ is set (though some sources note def is used for immutable bindings).
  • Collection Types: The language provides both mutable and immutable collections. Immutable collections use value semantics, while mutable collections (prefixed with @) use reference semantics.
  • Syntactic Sugar: Janet includes shorthand for anonymous functions (|(+ 1 $)), "splats" for argument spreading (;args), and flexible string literals using backticks to avoid escape sequences.

Community Insights and Trade-offs

Discussion among developers highlights several practical considerations when using Janet:

  • Performance: Users report very fast startup times, with one user noting a 1.4ms startup via hyperfine, making it a competitive alternative to dash (1ms) for system scripts.
  • Shell Integration: The sh third-party library provides a DSL for pipes and redirects (e.g., ($ find . -name *.janet | say)), which some users claim makes Janet a viable replacement for Bash, Python, or Awk for longer system scripts.
  • Critiques: Some users have noted a lack of package management versioning and a scarcity of advanced libraries (such as complex HTTP routing). Others have expressed a preference for static typing or a distaste for the pervasive use of parentheses common to Lisp dialects.

Sources