Janet 1.38.0-73334f3 Documentation
(Other Versions:
1.37.1
1.36.0
1.35.0
1.34.0
1.31.0
1.29.1
1.28.0
1.27.0
1.26.0
1.25.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.0
1.19.0
1.18.1
1.17.1
1.16.1
1.15.0
1.13.1
1.12.2
1.11.1
1.10.1
1.9.1
1.8.1
1.7.0
1.6.0
1.5.1
1.5.0
1.4.0
1.3.1
)
Special Forms
This document serves as an overview of all of the special forms in Janet.
Tuples are used to represent function calls, macros, and special forms. Most functionality is exposed through functions, some through macros, and a minimal amount through special forms. Special forms are neither functions nor macros — they are used by the compiler to directly express a low-level construct that cannot be expressed through macros or functions. Special forms can be thought of as forming the real 'core' language of Janet. There are only 13 special forms in Janet.
(def name meta... value)
This special form binds a value to a symbol. The symbol can be substituted for
the value in subsequent expressions for the same result. A binding made by
def
is a constant and cannot be updated. A symbol can be redefined to a
new value, but previous uses of the binding will refer to the previous value of
the binding.
(def anumber (+ 1 2 3 4 5))
(print anumber) # prints 15
def
can also take a tuple, array, table or struct to perform
destructuring on the value. This allows us to do multiple assignments in one
def
.
(def [a b c] (range 10))
(print a " " b " " c) # prints 0 1 2
(def {:x x} @{:x (+ 1 2)})
(print x) # prints 3
(def [y {:x x}] @[:hi @{:x (+ 1 2)}])
(print y x) # prints hi3
def
can also append metadata and a docstring to the symbol when in the
global scope. If not in the global scope, the extra metadata will be
ignored.
(def mydef :private 3) # Adds the :private key to the metadata table.
(def mydef2 :private "A docstring" 4) # Add a docstring
# The metadata will be ignored here because mydef is
# not accessible outside of the do form.
(do
(def mydef :private 3)
(+ mydef 1))
(var name meta... value)
Similar to def
, but bindings set in this manner can be updated using set.
In all other respects it is the same as def
.
(var a 1)
(defn printa [] (print a))
(printa) # prints 1
(++ a)
(printa) # prints 2
(set a :hi)
(printa) # prints hi
(fn name? args body...)
Compile a function literal (closure). A function literal consists of an optional name, an argument list, and a function body. The optional name is allowed so that functions can more easily be recursive. The argument list is a tuple of named parameters, and the body is 0 or more forms. The function will evaluate to the last form in the body. The other forms will only be evaluated for side effects.
Functions also introduce a new lexical scope, meaning the def
s and
var
s inside a function body will not escape outside the body.
(fn []) # The simplest function literal. Takes no arguments and returns nil.
(fn [x] x) # The identity function
(fn identity [x] x) # The name will make stacktraces nicer
(fn [] 1 2 3 4 5) # A function that returns 5
(fn [x y] (+ x y)) # A function that adds its two arguments.
(fn [& args] (length args)) # A variadic function that counts its arguments.
# A function that doesn't strictly check the number of arguments.
# Extra arguments are ignored.
(fn [w x y z &] (tuple w w x x y y z z))
# For improved debugging, name with symbol or keyword
(fn alice [] :smile)
(fn :bob [] :sit)
For more information on functions, see the Functions section.
(do body...)
Execute a series of forms for side effects and evaluates to the final form. Also introduces a new lexical scope without creating or calling a function.
(do 1 2 3 4) # Evaluates to 4
# Prints 1, 2 and 3, then evaluates to (print 3), which is nil
(do (print 1) (print 2) (print 3))
# Prints 1
(do
(def a 1)
(print a))
# a is not defined here, so fails
a
(quote x)
Evaluates to the literal value of the first argument. The argument is not
compiled and is simply used as a constant value in the compiled code. Preceding
a form with a single quote is shorthand for (quote expression)
.
(quote 1) # evaluates to 1
(quote hi) # evaluates to the symbol hi
(quote quote) # evaluates to the symbol quote
'(1 2 3) # Evaluates to a tuple (1 2 3)
'(print 1 2 3) # Evaluates to a tuple (print 1 2 3)
(if condition when-true when-false?)
Introduce a branching construct. The first form is the condition, the second
form is the form to evaluate when the condition is true, and the optional third
form is the form to evaluate when the condition is false. If no third form is
provided it defaults to nil
.
The if
special form will not evaluate the when-true or when-false forms
unless it needs to - it is a lazy form, which is why it cannot be a function or
macro.
The condition is considered false only if it evaluates to nil
or
false
- all other values are considered true
.
If when-true or when-false is evaluated, this is done in the context of a newly introduced lexical scope.
(if true 10) # evaluates to 10
(if false 10) # evaluates to nil
(if true (print 1) (print 2)) # prints 1 but not 2
(if 0 (print 1) (print 2)) # prints 1
(if nil (print 1) (print 2)) # prints 2
(if @[] (print 1) (print 2)) # prints 1
(splice x)
The splice
special form is an interesting form that allows an array or
tuple to be put into another form inline. It only has an effect in two places -
as an argument in a function call or literal constructor, or as the argument to
the unquote form. Outside of these two settings, the splice special form simply
evaluates directly to its argument x
. The shorthand for splice
is
prefixing a form with a semicolon. The splice
special form has no effect
on the behavior of other special forms, except as an argument to unquote
.
In the context of a function call, splice
will insert the contents
of x
in the parameter list.
(+ 1 2 3) # evaluates to 6
(+ @[1 2 3]) # bad
(+ (splice @[1 2 3])) # also evaluates to 6
(+ ;@[1 2 3]) # Same as above
(+ ;(range 100)) # Sum the first 100 natural numbers
(+ ;(range 100) 1000) # Sum the first 100 natural numbers and 1000
[;(range 100)] # First 100 integers in a tuple instead of an array.
(def ;[a 10]) # this will not work as def is a special form.
Notice that this means we rarely need the apply
function, as the
splice
operator is more flexible.
A splice
form can also be used as the argument to an unquote
form,
where it will behave like an unquote-splicing
special in Common Lisp.
Using the short form of both of these specials, this can be abbreviated
,;some-array-expression
.
(while condition body...)
The while
special form compiles to a C-like while
loop. The body
of the form will be continuously evaluated until the condition is false
or nil
. Therefore, it is expected that the body will contain some side
effects or the loop will go on forever. The while
loop always evaluates
to nil
.
Note that the while
special form introduces a new lexical scope.
(var i 0)
(while (< i 10)
(print i)
(++ i))
(break value?)
Break from a while
loop or return early from a function. The break
special form can only break from the inner-most loop. Since a while
loop
always returns nil
, the optional value
parameter has no effect when
used in a while
loop, but when returning from a function, the
value
parameter is the function's return value.
The break
special form is most useful as a low level construct for
macros. You should try to avoid using it in handwritten code, although it can be
very useful for handling early exit conditions without requiring deeply indented
code (try the cond
macro first, though).
# Breaking from a while loop
(while true
(def x (math/random))
(if (> x 0.95) (break))
(print x))
# Early exit example using (break)
(fn myfn
[x]
(if (= x :one) (break))
(if (= x :three) (break x))
(if (and (number? x) (even? x)) (break))
(print "x = " x)
x)
# Example using (cond)
(fn myfn
[x]
(cond
(= x :one) nil
(= x :three) x
(and (number? x) (even? x)) nil
(do
(print "x = " x)
x)))
(set l-value r-value)
Update the value of the var l-value
with the new r-value
. The
set
special form will then evaluate to r-value
.
The r-value
can be any expression, and the l-value
should be a
bound var or a pair of a data structure and key. This allows set to behave like
setf
or setq
in Common Lisp.
(var x 10)
(defn prx [] (print x))
(prx) # prints 10
(set x 11)
(prx) # prints 11
(set x nil)
(prx) # prints nil
(def tab @{})
(set (tab :property) "hello")
(pp tab) # prints @{:property "hello"}
(quasiquote x)
Similar to (quote x)
, but allows for unquoting within x
. This
makes quasiquote
useful for writing macros, as a macro definition often
generates a lot of templated code with a few custom values. The shorthand for
quasiquote is a leading tilde ~
before a form. With that form,
(unquote x)
will evaluate and insert x
into the unquote
form. The shorthand for (unquote x)
is ,x
.
(unquote x)
Unquote a form within a quasiquote
. Outside of a quasiquote
,
unquote
is invalid.
(upscope & body)
Similar to do
, upscope
evaluates a number of forms and sequence and evaluates to the
result of the last form. However, upscope
does not create a new lexical scope, which means
that bindings created inside it are visible in the scope where upscope is declared. This is
useful for writing macros that make several def and var declarations at once.
(upscope
(def a 1)
(def b 2)
(def c 3)) # -> 3
(+ a b c) # -> 6
In general, use this macro as a last resort. There are other, often better ways to do this, including using destructuring.
(def [a b c] [1 2 3])