Some demos of Janet features listed at the main page, in order, on a Linux box.
Links to official docs should be provided along the way, highly recommended to check them out to start the assimila^H^H^H^H^H^H^H^H familiarization process :)
Hint: if reading this on GitHub, consider using the Table of Contents icon to navigate to different sections.
The intent is to touch on the features listed at the main page. This is a work-in-progress. Currently the list includes:
- Minimal setup - one binary and you are good to go!
- Builtin support for threads, networking, and an event loop
- First class closures
- Garbage collection
- First class green threads (continuations)
- Mutable and immutable arrays (array/tuple)
- Mutable and immutable hashtables (table/struct)
- Mutable and immutable strings (buffer/string)
- Macros
- Tail call optimization
- Direct interop with C via abstract types and C functions
- Dynamically load C libraries
- Lexical scoping
- REPL and interactive debugger
- Parsing Expression Grammars built in to the core library
- 500+ functions and macros in the core library
- Export your projects to standalone executables with a companion build tool, jpm
- Add to a project with just janet.c and janet.h
Note that this list is not quite the same as the one in the repository README.
Some additional items from the longer list:
- Configurable at build time - turn features on or off for a smaller or more featureful build
- Python-style generators (implemented as a plain macro)
One way to experience this is to download one of the files from the GitHub releases page.
At the time of this writing, for a Linux environment, I got janet-v1.25.1-linux-x64.tar.gz, uncompressed it, and was able to run the janet binary that lived in the bin subdirectory:
Personally, I prefer a non-root local install of the master branch (later content in this document will be using the local installation), but may be the earlier method is better if you want a quick taste. Some distributions carry packages, but I'm not sure how up-to-date they are.
Here's a snippet from the Multithreading docs:
Trying that at the repl:
Not so clear in text perhaps -- I pressed the Enter key after the Thread one text appeared for aesthetic purposes.
Let's ask for some help from our snake friend:
Here's a snippet adapted from the Networking docs:
Trying that at the repl:
Servers are doable too.
Here is some code from The Event Loop docs:
Pasting the above to the repl a bit at a time.
First we make a function:
We pass the function to ev/call which gives us back a fiber.
To get it to start, we need to let it run by temporarily relinquishing control via ev/sleep:
Note the bob working 0....
Now arrange for another:
Relinquish control again:
I was able to paste (print "Everyone should be done by now!") while output was appearing from ongoing activity. Notice how it appears between the text sally working 12... and sally working 13....
We'll make an "adder" maker:
Now use it to make an adder and call it:
Finally, pass the adder to another function for use:
Not sure how to demonstrate this...how about some evidence?
The Janet's Memory Model docs have some details.
In Janet, I believe these are called "fibers".
Adapting some code from the beginning of the Fibers docs:
Stepping through:
Can get the status of a fiber:
Resume the fiber ("resume" also means start):
Resume 3 more times:
Check the fiber's status:
Resume it again:
Check the status again:
What if resume is called now?
Oops :)
See the Fibers docs and the Fiber Module docs for more info.
There are two kinds of array-like data structures in Janet, mutable (array) and immutable (tuple).
The Arrays docs cover mutable arrays. In Janet, "array" refers to the mutable type of array data structure.
Mutability is typically expressed using a leading @ character:
Parentheses can be used as well, but this does not appear common:
Let's add something:
Now get something back:
...in a number of other ways:
get and in are ordinary functions.
See the Array docs and the Array Module docs for more info.
The Tuples docs cover imumutable arrays. In Janet, "tuple" refers to an immutable type of array data structure. Note that this is not a "persistent" data structure as in Clojure.
Let's make a tuple:
It's also possible to make a tuple "directly" using parentheses, but quoting is necessary:
Retrieval can be done like:
or in a number of other ways:
get and in are ordinary functions.
See the Tuples docs and the Tuple Module docs for more info.
There are two kinds of hash table / associative array-like data structures in Janet, mutable (table) and immutable (struct).
The Tables docs cover mutable hash tables. In Janet, "table" refers to the mutable type of hash table data structure.
Mutability is typically expressed using a leading @ character:
Let's add something:
To get something out:
This can also be accomplished by:
Note that the following doesn't work in the same way as for arrays and tuples:
For a hint as to why this is, see the Object-Oriented Programming docs. In short, (:a a-table) becomes ((get a-table :a) a-table) which in turn leads to (1 a-table).
So if 1 were added to the table as a key with an associated value:
and another attempt is made, we would get:
get and in are ordinary functions.
See the Tables docs and the Table Module docs for more info.
The Structs docs cover immutable hash tables. In Janet, "struct" refers to an immutable type of hash table data structure. Note that this is not a "persistent" data structure as in Clojure.
Let's make a struct:
Retrieval can be done like this:
or by either of:
Note that the following doesn't work in the same way as for arrays and tuples:
For a hint as to why this is, see the Object-Oriented Programming docs. In short, (:a a-struct) becomes ((get a-struct :a) a-struct) which in turn leads to (1 a-struct).
If a-struct had been defined to contain a key of value 1 with an associated value:
and an attempt similar to the earlier one is made, we should get:
get and in are ordinary functions.
See the Structs docs and the struct-related calls starting at the docs for struct for more info.
There are two kinds of string-like data structures in Janet, mutable (buffer) and immutable (string).
The Buffers docs cover mutable strings. In Janet, "buffer" refers to a mutable type of string data structure.
Mutability is typically expressed using a leading @ character:
Let's add a character to the end of the buffer:
How about multiple characters at once?
Retrieval of an individual character can be done by:
Or:
A "sub" buffer (though it's a different buffer) can be retrieved by:
get and in are ordinary functions.
See the Buffers docs and the Buffer Module docs for more info.
The Strings docs cover immutable strings. In Janet, "string" refers to the immutable type of string as in a number of other programming languages.
One can make a string by:
To get a character:
Or:
Substrings can be retrieved by:
get and in are ordinary functions.
See the Strings docs and the String Module docs for more info.
Adapted from the Macros docs:
Let's try it out.
Define the macro first:
Now call it:
Note that unlike Common Lisp, Scheme, or Clojure, Janet uses ~ for quasi-quoting / syntax-quoting. There is a corresponding special form named quasiquote.
; is an abbreviation for using the splice special form.
, is an abbreviation for using the unquote special form.
See the Special Forms docs for more details about ~ (quasiquote), ; (splice) , and , (unquote).
For more info on macros, see the Macros docs.
As a placeholder, here is some evidence:
- https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/core/vm.c#L379
- https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/core/vm.c#L1008-L1052
- https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/core/vm.c#L270-L276
- https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/core/vm.c#L606-L616
See The Janet C API docs for more details.
There may be more than one way to interpret what this means. Since this item has been in the list longer than the recent addition of Janet's FFI capability, perhaps it didn't use to refer to FFI.
I think this refers to Janet's native module capability.
For a smallish example, see spork's utf8 module, and perhaps start by looking at the following code at the bottom of the file:
Then take a look at:
Pieces that might be worth investigating further include:
- janet_fixarity
- janet_getinteger
- janet_wrap_integer
See the Writing a Module section of The Janet C API docs, the Writing C Functions docs, and the Wrapping Types docs for details.
For a taste, consider the following from the FFI docs:
First, the path to the dynamic library (note that nil means to use the current binary) to bind is specified via ffi/context:
Use ffi/defbind to generate bindings for a native function (in this case memcpy). We specify information about the name, return type and parameters of the function:
Note that the return value of ffi/defbind is a function. This new function will be used shortly.
Now prepare some buffers, buffer1 as a destination and buffer2 as a source:
Note that buffer1 is initially @"aaaa".
Invoke memcpy (the function newly defined by ffi/defbind above):
This should have copied from buffer2 to buffer1. The return value is not interesting in this case so we ignore it.
Now observe the content of buffer1:
Note that the original "aaaa" is gone (nil is the return value of calling print).
The content of buffer could have been determined as follows too:
For a more in-depth example, see the gtk example.
Note, I had to tweak the location of libgtk-3.so in gtk.janet to be:
but after that, I had success via the invocation:
See the Foreign Function Interface docs and the FFI Module docs for more information.
Does the following count as evidence?
Calling janet at the command line gives a repl:
There is a sometimes-helpful delimiter reminder feature:
Note that in the repl's prompt after the last colon : character and before the greater-than > character, there are one or more opening delimiters in some of the above lines:
These are reminders that there are unmatched opening delimiters waiting to be closed.
Simple completion is possible via the Tab key:
To get the above output, I pressed the Tab key after typing "(do".
There is a way to quickly see docstrings for various things:
After typing "array/push", I entered the sequence Ctrl-G (holding the Ctrl key down and pressing and releasing the G key).
Some other key sequences such as Ctrl-A, Ctrl-E, etc. behave in similar ways to what one might use in a shell such as bash. The janet(1) man page has a longer listing. I don't know of a nicely viewable list of the key sequences on the web, but there's this bit from the main repository.
One way to get access to the bytecode debugger is to use the -d command line option to janet, then use the debug function:
Putting a call to debug in a function and calling that function can be handy too:
We can get a bytecode listing via .ppasm:
Execute one bytecode instruction via .step:
...and keep going to see the function return:
Note that the value 9 is the return value of the function call.
When we're done we can exit the debugger to return to the original repl context:
See this part of boot.janet for which functions are available for the debugger and The Janet Abstract Machine docs for more on Janet bytecode and bytecode interpreter.
Also of interest might be the Debug Module docs.
From the Parsing Expression Grammars docs is:
Note that this expression refers to a plain Janet struct.
Let's define it at the repl:
Typically one might use this via the function peg/match:
A nil return value means there was no match.
For a match:
One gets back an array of captures. In this case there was a match, but nothing was captured, so the array is empty.
For an example of a capturing PEG expression, try:
The first 3 characters were captured as a unit and returned as an element of the "capture stack".
A slightly more complicated example is:
The PEG might be more readily perceived as:
Above, initial whitespace is skipped (matched but not captured), a sequence of digits is captured, more whitespace is skipped and then finally a sequence of digts is captured again.
Note that there are 2 captures on the capture stack "12" and "89" and the trailing period has not been captured.
Also note that the definition of ip-address from earlier can be condensed (and the key-value pairs reordered) as follows:
* is an alias for sequence.
+ is an alias for choice.
:d is defined in default-peg-grammar...along with a number of other things:
FWIW, its definition in boot.janet is prettier.
Especially while learning I recommend using the longer names as:
-
some of the aliases might be confusing if you are used to regular expressions (I'm looking at you * and + -- though I take it these are based on those from Lua's LPEG), and
-
if you return to looking at Janet code after not having looked in a while you might not immediately recall the aliases
See the Parsing Expression Grammars docs and the PEG Module for official docs.
Highly recommended is pyrmont's excellent How-To: Using PEGs in Janet article.
For simple examples and docs, follow individual links at this listing of the PEG specials along with some extracted real-world usage.
Yes, that last bit is a shameless plug :)
See the Core API docs, but also JanetDocs, a community documentation site.
Install jpm first.
Create a new project:
Answer some questions (if you want):
Look inside:
Make an executable with jpm's quickbin subcommand:
Try it out:
Get curious:
See the jpm docs, the jpm repository, and the generated JPM reference for more info.
Some examples of doing this (or similar) include:
- The janet-lang.org website
- ahungry's puny-gui - a small cross-platform (native) GUI setup (GNU/Linux + Windows)
- cfoust's cy - time travel in the terminal (mostly in go)
- greenfork's jzignet - Zig library to connect Janet and Zig together
- gwegash's trane - a lispy livecoding environment
- ianthehenry's bauble - for composing signed distance functions in a high-level language (Janet), compiling them to GLSL, and rendering them via WebGL
- MikeBeller's janet-playground - A WebAssembly based playground for Janet
- s5bug's sys-script - Controlling the Nintendo Switch with Lisp
- sepisoad's super-janet-typist - a short typing game made with janet lisp
- yumaikas' janet-peg-playground - A WebAssembly based playground for Janet's peg/match (demo site)
- zacharycarter's voodoo - 3D game programming and rapid prototyping library
- jaylib-wasm-demo - A demo of using jaylib in a web browser
See The Janet C API docs and the Embedding docs for more details.
-
Configurable at build time - turn features on or off for a smaller or more featureful build
See the Configuration docs.
-
Python-style generators (implemented as a plain macro)
See the generate macro and at least one example at JanetDocs.