Janet Notes and Examples

Janet Language Basics

Janet in 2 minutes at Mach 1 from 10_000 meters. For the full official story, go straight to the Janet docs. See also JanetDocs which includes community-supplied examples.

# A comment.

Literals

The type is shown in the comment:

nil                 # :nil
true  false         # :boolean
0  1  3_000_000     # :number
1.5  2e3  4e-2      # :number
:to-wit   :a/b      # :keyword
who?  me!  a/b   _  # :symbol

Typically, _ is used as a dummy variable.

Symbols evaluate to whatever they point/refer to. To stop them from being evaluated, you must 'quote-them.

Note that slashes in symbols and keywords are just semantic — there’s no namespace magic happening.

Strings

"foo bar"

# So-called longstrings. Use 1 or more matching backticks.
# Backslash-escapes don't work in longstrings.
``a multi-line
string here``

(string "con" "cat" "enate")

# To get a substring:
(string/slice "abcdefgh" 2 5)  #=> "cde"
# To find a substring:
(string/find "de" "abcdefgh")  #=> 3

See the string standard library for more (splitting, replacement, etc.)

Types of things in Janet

Objects can be recursive (like data structures) or non-recursive (like booleans, numbers, strings, keywords, symbols).

Data structures include tuples, arrays, structs, and tables.

Strings and data structures in Janet come in two varieties: mutable and immutable. Mutables are prefixed with @. Summarized briefly:

# Both are bytes:
 "foo"  # string (immutable)
@"foo"  # buffer  (mutable)

# Both are indexed:
 [1 2]  # tuple (immutable)
@[1 2]  # array  (mutable)

# Both are dictionary:
 {:a 1 :b 2}  # struct (immutable)
@{:a 1 :b 2}  # table   (mutable)

See also indexed? and dictionary?. The bytes? function tells you if its arg is either a string, buffer, keyword, or symbol.

Summarizing some of those:

  • Symbols, keywords, strings, and buffers are all byte sequences (8-bit clean).
  • Symbols, keywords, and strings are all immutable.
  • describe gets you a human-readable, “not-recursive” string (so, a byte sequence) description of a value. If the value is a data structure type, the string you get back contains a unique id (pointer).
  • Strings and keywords evaluate to themselves.
  • Symbols evaluate to whatever they’re bound to.
  • Of course, when a symbol is bound to a string, the symbol and the string it points to are both byte sequences.

There’s no literal for sets — use a set library like janet-set (note that it just gives you a thin interface over tables or structs).

You can get the type of any object via (type the-obj) (returns a keyword — see top of this file).

Types come with their own like-named constructor functions:

  • symbol, keyword, string, buffer: they first (if necessary) convert their args to byte sequences using describe, then they concat.
  • tuple, array, struct, table

Note that although symbols, keywords, strings, and buffers are all themselves bytes, symbols usually refer to (and evaluate to) other objects.

Truthiness and Falsiness

Only nil and false are falsey. Everything else is truthy.

Defining Things

(def x 1)  # 'x must always point to 1
(var y 2)  # You can change to what `y` points.

# You can't make `z` point to anything else, but the object it points
# to here happens to be mutable.
(def z @[1 2])

Changing/Setting Things

(var x 3.0)
(++ x)
(+= x 5.0)
(set x 99.9)

See also the section on data structures.

Comparison

Immutables are compared by comparing their values, but mutables are compared by pointer.

repl:> (=  [1 2 [:a :b]]  [1 2 [:a :b]])
true
repl:> (= @[1 2 [:a :b]] @[1 2 [:a :b]])
false

See deep= for deep comparison of mutables.

Id

You can see the unique “id” of a recursive object by calling (string foo) on the object. (See below for pretty-printing.)

Control Structures

do provides a new local scope and lets you execute a series of possibly side-effecting expressions within it. It evaluates to its last value.

let, when, and function bodies provide an “implicit-do”.

(if expr this else-that)
(while stays-true this that other) # Can `break` out.
cond
case
when
for   # C-style looping
loop  # general looping
seq   # list comprehension

For looping, see the data structures chapter.

Functions

(fn [x] (* x 2))          # Anonymous function.
(fn doubler [x] (* x 2))  # Somewhat less-anonymous function.

# Garden variety function definiton.
(defn doubler
  ``Docstring goes here
  thank you very much.``
  [x]
  (* x 2)

Also supported:

  • variadic functions (extra args are packed into a tuple)
  • destructuring
  • defn- for not-to-be-exported functions

To call functions in the standard library, you don’t need to import anything; just go ahead and call them, ex., (array/concat ...).

Note that “cfunction”s are just functions Janet uses which are implemented in C (for an example, see (doc os/time)).

Sorting

Note that sort and sort-by sort in-place, whereas sorted and sorted-by return a new sorted array. See also the Janet docs on comparison operators.

Pretty-Printing & Formatting

Printing:

(print x) prints x
(printf "%j" x) prints in jdn format
(printf "%M" x) prints nicely-formatted
(pp x) pretty-prints x, configurable

Formatting Strings:

(describe x) human-readable byte sequence
(string/format ...) see printf above
(buffer/format ...) same, but for buffers

For serializing, see marshal and unmarshal.

Top-Level Functions

Janet comes with a luxurious selection of rich corinthian top-level functions.

Possibly todo

  • splice ;
  • dynamic bindings
  • Environments & Contexts
  • Fibers
  • Macros