Loading Janet… Fetching WASM runtime

Values & Types

Everything is a value. Most things are immutable by default.

Janet has the usual suspects — numbers, strings, booleans, nil — plus a few that feel distinctly Lispy: keywords (like :this) that evaluate to themselves and are cheap to compare, and symbols that refer to bindings.

The interesting part is the collection types. Janet gives you two flavours of each: mutable (prefixed with @) and immutable:

  • Arrays @[1 2 3] vs Tuples [1 2 3]
  • Tables @{:a 1} vs Structs {:a 1}
  • Buffers @"hello" vs Strings "hello"

Immutable values can be used as table keys. Mutable ones cannot. pp pretty-prints any value — it is your best friend when exploring.

janet
Click Run to see output
Ctrl+Enter to run  |  edit freely

Functions

First-class, closures included, with a terse shorthand syntax.

defn defines a named function. fn is anonymous. The pipe shorthand |(* $ 2) is perfect for one-liners — $ is the single argument.

Janet has the standard functional toolkit: map, filter, reduce, each. But the threading macros are where code starts to read like prose. -> threads the value as the first argument; ->> threads as the last. They eliminate deeply nested calls.

Closures work exactly as you expect — functions capture their surrounding scope.

janet
Click Run to see output
Ctrl+Enter to run  |  edit freely

PEG Grammars

Parsing Expression Grammars, built into the language. No library needed.

Most languages require a parsing library. Janet ships PEG as a core primitive. You describe a grammar as a data structure — no separate compilation step, no DSL file — and peg/match runs it against a string.

Built-in character classes like :d (digit), :a (letter), :s (whitespace) keep patterns compact. capture extracts substrings. Named rules in a quasiquoted struct ~{:rule ...} let you build composable grammars.

peg/match returns an array of captured values, or nil if the pattern does not match. This makes it natural to use in conditionals.

janet
Click Run to see output
Ctrl+Enter to run  |  edit freely

Fibers

Pauseable functions. The foundation of generators, coroutines, and async.

A fiber is a function that can pause itself mid-execution with yield and be resumed later. Between yields, the fiber's entire stack is preserved. This is cooperative multitasking without callbacks or promises.

You create a fiber with fiber/new and run it with resume. Each call to resume runs until the next yield (or until the function returns, after which the fiber is dead).

The :generate verb in loop consumes a fiber like an iterator — the cleanest way to work with lazy sequences.

janet
Click Run to see output
Ctrl+Enter to run  |  edit freely

Free REPL

The full Janet runtime, right here. Everything you define persists across runs.

This is the same Janet interpreter that powered all the examples above. State is shared — a defn you write here is callable in the next run. Ctrl+Enter to run, ↑↓ for history.