Jump to API / list of operators
Examples in these docs are shown as if typed at the l1
"REPL"
(read-eval-print-loop). To leave the REPL, type Control-D or
(exit)
:
$ l1
> (exit)
$
If you want arrow keys for next/previous command or to move forward or
backward on the line, history, etc., wrap l1
with the rlwrap
command (installed using your favorite package manager), e.g.:
$ rlwrap l1
>
Expressions in l1
are atoms, lists, numbers, or functions:
Atoms have a name, such as x
, foo
, or Eisenhower!
, and can be
"bound to a value in the current environment," meaning,
essentially, assigned a value. For example, we can bind a value to a
and retrieve it:
> (def a 1)
> a
1
>
Lists are collections of zero or more expressions. Examples:
(getting atomic)
(10 9 8 7 6 5 4 3 2 1)
(+ 2 2)
()
In general, lists represent operations whose name is the first element
and whose arguments are the remaining elements. For example, (+ 2 2)
is a list that evaluates to the number 4
. To prevent evaluation
of a list, prepend a quote character:
> '(+ 2 2)
(+ 2 2)
> (+ 2 2)
4
As in most Lisps, lists are actually implemented "under the hood" as "cons pairs" (Wikipedia page). The list
(1 2 3)
actually represented internally as
(1 . (2 . (3 . ())))
where
(a . b)
is the same as
(cons a b)
In practice, the dot notation is uncommon in l1
programs, except
when used to represent rest arguments, described below.
Numbers are integer values and can be of arbitrary magnitude:
0
999
7891349058731409803589073418970341089734958701432789
Numbers evaluate to themselves:
> 0
0
> 7891349058731409803589073418970341089734958701432789
7891349058731409803589073418970341089734958701432789
Numbers and atoms can be turned into lists:
> (split 'atomic)
(a t o m i c)
> (split 1234)
(1 2 3 4)
And lists can be turned into atoms or numbers:
> (fuse '(getting atomic))
gettingatomic
> (fuse '(10 9 8 7 6 5 4 3 2 1))
10987654321
In l1
, the empty list ()
is the only logical false value; everything
else is logical true. Logical true and false are important when evaluating
conditional statements such as if
, when
, or cond
. The default
logical true value is t
. t
and ()
evaluate to themselves.
The and
, or
and not
operators work like they do in most other
languages:
> (and t t)
t
> (or () ())
()
> (or () 135987)
135987
> (and () (launch missiles))
()
> (not t)
()
> (not ())
t
> (not 13987)
()
> (if ()
(launch missiles)
555)
555
Unlike many modern languages, l1
doesn't have strings. Instead,
atoms and lists are used where strings normally would be:
> (printl '(Hello, world!))
Hello, world!
()
(In this example, the Hello, world!
is output to the terminal, and
then the return value of printl
, namely ()
.)
Atom names can be arbitrarily long (they are simply Go strings under
the hood). When l1
parses your code, it will interpret any UTF-8-encoded unicode characters
but the following as the start of an atom:
0123456789+-. \t\n\r()~@#;`'
After the first character, anything is allowed except spaces or
\t\n\r()~@#;`'
Deviations from these constraints need special handling. For example:
> (printl '(@Hello, world!))
(...
(unexpected lexeme 'unexpected character '@' in input' on line 1))
A workaround is to use syntax quote and unquote, described below, to
dynamically create a new atom name using the BANG
alias for !
:
> (printl `(~(fuse `(~ATSIGN Hello,)) world!))
@Hello, world!
()
This is admittedly awkward, but rare in practice for the kinds of
programs l1
was designed for. BANG
is one of a small set of atoms
helpful for this sort of construction:
BANG
COLON
COMMA
NEWLINE
PERIOD
QMARK
SPACE
TAB
QUOTE
DQUOTE
BQUOTE
UPLINE
These all evaluate to atoms whose names are the unreadable characters, some of which may be helpful for text games and other diversions:
> (dotimes 10
(println
(fuse
(repeatedly 10
(lambda ()
(randchoice (list COMMA
COLON
PERIOD
BANG
QMARK)))))))
.!!!.???..
,??::?!,.?
?,?!?..:!!
,:.,?.:!!!
!!:?!::.,?
,:!!!:,!!:
,???:?!:!?
.,!!?,!:!?
!:,!!!.:!:
??.,,:.:..
()
>
The UPLINE
character, in particular, which moves the cursor to the
beginning of the previous line, is useful for making command-line
output which repeatedly updates the line in place, rather than
printing multiple lines.
Functions come in two flavors: temporary functions, called "lambda" functions for historical reasons, and functions which are defined with a name and kept around in the environment for later use. For example,
> (defn plus2 (x) (+ x 2))
> (plus2 3)
5
> ((lambda (x) (* 5 x)) 3)
15
Since function names are atoms, their names follow the same rules for atoms given above. The following function definitions are all equally valid:
> (defn increase! (x) (* x 1000))
()
> (defn 增加! (x) (* x 1000))
()
> (defn մեծացնել! (x) (* x 1000))
()
Functions can take a fixed number of arguments plus an extra "rest" argument, separated from the fixed arguments with a "."; the rest argument is then bound to a list of all remaining arguments:
> (defn multiply-then-sum (multiplier . xs)
(* multiplier (apply + xs)))
()
> (multiply-then-sum 5 1)
5
> (multiply-then-sum 5 1 2 3)
30
A function that has a rest argument but no fixed arguments is specified using the empty list as its fixed argument:
> (defn say-hello (() . friends)
(list* 'hello friends))
> (say-hello 'John 'Jerry 'Eden)
(hello John Jerry Eden)
In addition to the functions described above, some l1
functions are
"built in" (implemented in Go as part of the language core). Examples
include car
, cdr
, cons
, etc. The API Docs below specify whether
a function is built-in or not.
One special family of predefined functions not shown in the API docs
(because they are effectively infinite in number) is extensions of
car
and cdr
:
> (car '(1 2 3))
1
> (cadr '(1 2 3))
2
> (caddr '(1 2 3))
3
> (caar '((one fish) (two fish)))
one
> (caadr '((one fish) (two fish)))
two
> (cadar '((one fish) (two fish)))
fish
(cadr x)
should be read as (car (cdr x))
, and so on.
Functions may invoke themselves recursively:
> (defn sum-nums (l)
(if-not l
0
(+ (car l) (sum-nums (cdr l)))))
()
> (sum-nums '(0 1 2 3 4 5 6 7 8 9))
45
The above function performs an addition after it invokes itself. A function which invokes itself immediately before returning, without doing any more work, is called "tail recursive." Such functions are turned into iterations automatically by the interpreter ("tail call optimization"). The above function can be rewritten into a tail-recursive version:
> (defn sum-nums-accumulator (l acc)
(if-not l
acc
(sum-nums-accumulator (cdr l) (+ acc (car l)))))
()
> (sum-nums-accumulator '(0 1 2 3 4 5 6 7 8 9) 0)
45
Lambda functions can invoke themselves if given a name directly before
the parameters are declared. We can rewrite the above function to
hide the acc
argument from the user:
> (defn sum-nums (l)
(let ((inner (lambda inner (l acc)
(if-not l
acc
(inner (cdr l) (+ acc (car l)))))))
(inner l 0)))
()
> (sum-nums '(0 1 2 3 4 5 6 7 8 9))
45
In this version, inner
is tail-recursive, and sum-nums
is now as
convenient to use as our first, non-tail-recursive version was.
In addition to the basic conditional statements cond
, if
,
if-not
, when
, and when-not
, flow of control is generally
implemented via recursion, as it is in Scheme, and inspection of its
core library
l1.l1
will
show several examples of recursive functions being used as the primary
recurrence method. A few other control flow methods are also
available: while
, which loops so long as a condition is
true; dotimes
, which executes a body of statements a
given number of times; foreach
, which executes a body of
statements for each element in a loop; and loop
, which
loops forever. Macros can be used to create new control abstractions;
however, possibilities are somewhat restricted compared to some
languages, due to the inability of l1
to handle branches or "goto"
statements.
There is currently one kind of assertion expression in l1
, namely
the is
macro:
> (is (= 4 (+ 2 2)))
> (is ())
ERROR:
((assertion failed: ()))
>
If the argument to is
is logical false (()
), then an error is printed
and the program exits (if running a program) or the REPL
returns to the top level prompt.
If is
is checking an equality of two items which fails, the macro is smart
enough to print out a more detailed error report showing the two
expressions and their values:
> (is (= 5 (+ 1 1)))
ERROR:
((expression 5 ==> 5 is not equal to expression (+ 1 1) ==> 2))
>
If desired, an error can be caused deliberately with the error
function:
> (defn checking-len (x)
(if (list? x)
(len x)
(error '(argument must be a list))))
()
> (checking-len 3)
ERROR:
((argument must be a list))
>
In some situations, such as during automated tests, it may be
desirable to ensure that a specific error is raised. The errors
special form takes a list argument and a body, then checks to ensure
that the body raises an error which contains the supplied list:
> (errors '(division by zero) (/ 1 0))
()
> (errors '(division) (/ 1 0))
()
> (errors '(rocket crashed) (/ 1 0))
ERROR:
((error 'rocket crashed' not found in '((builtin function /) (division by zero))'))
> (errors '(division by zero) (* 1 0))
ERROR:
((error not found in ((quote (division by zero)) (* 1 0))))
>
Most contemporary languages will print a stacktrace when an error
occurs. l1
stacktraces are somewhat rudimentary: in keeping with
the rest of the language, they are simply lists. To capture an error
occurring within a body of code, wrap the body in a try
statement
and add a catch
clause, as follows:
> (try
(printl '(got here))
(+ 3
(/ 1 0))
(printl '(did not get here))
(catch e
(cons '(oh boy, another error)
e)))
got here
((oh boy, another error) (builtin function /) (division by zero))
>
The exception e
is a list of lists to which items are added (in the
front) as the error returns up the call chain. As an ordinary list,
it can be manipulated like any other, as shown above using cons
.
An important caveat is that, since tail recursion is optimized away, many "stack frames" (or their equivalent) are optimized away - there is no way to track the entire history in detail without losing the space-saving power of the optimization. Nevertheless, the generated exception can be helpful for troubleshooting.
There is, currently, no equivalent of the finally
clause one sees in
Java or Clojure.
Rarely, one may wish to swallow any errors and continue execution.
swallow
will execute all the statements in its body and return t
if and only if any of them causes an (uncaught) error:
> (swallow (/ 1 0))
t
> (swallow (+ 1 1))
()
>
swallow
is used mainly in the fuzzing tests for l1
(see the
examples directory).
The shell
function executes a subprocess command, which should be a
list of atoms and numbers, and returns the result in the following
form:
((... stdout lines...)
(... stderr lines...)
...exit code..)
Examples (output reformatted for clarity):
> (shell '(pwd))
(((/Users/jacobsen/Programming/go/l1))
(())
0)
> (shell '(ls))
(((Dockerfile) (LICENSE) (Makefile) (README.md) (api.md)
(atom.go) (builtin.go) (builtin_test.go) (bumpver)
(cons.go) (core.go) (doc.go) (env.go) (env_test.go)
(error.go) (error_test.go) (eval_test.go) (example.l1)
(examples) (examples.txt) (go.mod) (go.sum) (intro.md)
(l1) (l1.html) (l1.jpg) (l1.l1) (l1.md) (lambda.go)
(lex.go) (lex_test.go) (lisp.go) (main.go) (math.go)
(math_test.go) (parse.go) (parse_test.go) (sexpr_test.go)
(shell.go) (state.go) (term.go) (tests.l1) (updatereadme.py)
(util.go) (version.go))
(())
0)
> (shell '(ls -al l1.l1))
(((-rw-r--r-- 1 jacobsen staff 16636 Sep 3 11:28 l1.l1)) (()) 0)
> (shell '(ls /watermelon))
((()) ((ls: /watermelon: No such file or directory)) 1)
For those familiar with macros (I recommend Paul
Graham's On Lisp for those who are not), l1
macros are
non-hygienic by
default. Gensyms, unique atom names useful for writing safe macros,
are available via the gensym
built-in function:
> (gensym)
<gensym-0>
> (gensym 'foo)
<gensym-foo-1>
The traditional syntax-quote
, unquote
, and splicing-unquote
are available, and
have sugared equivalents:
(let ((name 'larry)
(names '(moe curly)))
(syntax-quote (hello, (unquote name) as well as (splicing-unquote names))))
;;=>
(hello, larry as well as moe curly)
is the same as
(let ((name 'larry)
(names '(moe curly)))
`(hello, ~name as well as ~@names))
In addition to the quote ('
), syntax-quote (`
), unquote (~
),
and splicing unquote (~@
) shortcuts, the shortcut #_
is available, which is equivalent to (comment ...)
, e.g.,
#_(this is a commented form)
is equivalent to
(comment (this is a commented form))
l1
has a few built-in functions for creating simple text UIs:
screen-clear
: Clear the screenscreen-get-key
: Get a keystrokescreen-write
: Write a list, without parentheses, to anx
andy
position on the screen.with-screen
(macro): Enter/exit "screen" (UI) mode
The screen-...
functions must occur within a with-screen
expression. An example
program
shows these functions in action.
screen-get-key
will return a single-character atom whose name matches the exact key pressed, except for the following keys:
INTR
: interrupt (control-C)EOF
: end-of-file (control-D)CLEAR
: clear screen (control-L)BSP
: backspaceDEL
: deleteDOWNARROW
: down arrowEND
: end keyLEFTARROW
: left arrowRIGHTARROW
: right arrowUPARROW
: up arrowENTER
: enter/returnESC
: escape key
An example use of the special keys could be to exit a loop if the user types control-C or control-D:
(with-screen
(let ((continue t))
(while continue
;; Do something
(let ((k (screen-get-key)))
(cond
;; Exit if control-C or control-D:
((or (= k 'INTR)
(= k 'EOF))
(set! continue ()))
;; Handle other keys...
)))))
There are four ways of executing a source file, e.g. main.l1
:
- (Unix/Linux/Mac only) Add a shebang at the beginning of the script;
- Pass
main.l1
as an argument tol1
:l1 main.l1
, again at the shell prompt. - Call the
load
function from thel1
REPL or from within another file:(load main.l1)
. - "Compile" the source into an executable binary using
l1c
.
Option 3. is currently the only way of combining multiple source files
into a single program. There is currently no packaging or namespacing
functionality in l1
.
Programs can be run by giving the program name as an argument to l1
:
l1 hello.l1
However, if you add a "shebang" line
#!/usr/bin/env l1
at the beginning of an l1
file:
#!/usr/bin/env l1
;; hello.l1
(printl '(hello world))
and set the execute bit on the file permissions:
chmod +x hello.l1
then you can run hello.l1
"by itself," without explicitly invoking l1
:
$ ./hello.l1
hello world
$
A script l1c
is provided which allows one to build a stand-alone
binary from an l1
program. It requires a working Go installation
and probably only works on Linux / Mac / Un*x machines. Example:
$ cat hello.l1
(printl '(Hello, World!))
$ l1c hello.l1 -o hello
/var/folders/v2/yxn9b9h93lzgjsz645mblbt80000gt/T/tmp.QecBxTkp ~/Somedir/l1
go: creating new go.mod: module myprog
go: to add module requirements and sums:
go mod tidy
go: finding module for package github.com/eigenhombre/l1/lisp
go: found github.com/eigenhombre/l1/lisp in github.com/eigenhombre/l1 v0.0.56
~/Somedir/l1
...
$ ls -l hello
-rwxr-xr-x 1 jacobsen staff 3750418 Feb 14 10:46 hello
$ ./hello
Hello, World!
$
Because l1c
is a Bash script tied to the Go language build system,
you should install l1
via the go
tool
rather than via a downloaded
binary
if you wish to use l1c
.
If you are using Emacs, you can set it up to work with l1
as an "inferior
lisp" process as described in the Emacs manual.
I currently derive a new major mode from the base lisp-mode
and bind a few
keys for convenience as follows:
(define-derived-mode l1-mode
lisp-mode "L1 Mode"
"Major mode for L1 Lisp code"
(setq inferior-lisp-program (executable-find "l1")
(paredit-mode 1)
(put 'test 'lisp-indent-function 1)
(put 'testing 'lisp-indent-function 1)
(put 'errors 'lisp-indent-function 1)
(put 'if 'lisp-indent-function 1)
(put 'if-not 'lisp-indent-function 1)
(put 'foreach 'lisp-indent-function 2)
(put 'when-not 'lisp-indent-function 1)
(define-key l1-mode-map (kbd "s-i") 'lisp-eval-last-sexp)
(define-key l1-mode-map (kbd "s-I") 'lisp-eval-form-and-next)
(define-key l1-mode-map (kbd "C-o j") 'run-lisp))
(add-to-list 'auto-mode-alist '("\\.l1" . l1-mode))
If l1
has been installed on your path, M-x run-lisp
or using the appropriate
keybinding should be enough to start a REPL within Emacs and start sending
expressions to it.
135 forms available:
*
**
+
-
/
<
<=
=
>
>=
abs
and
apply
atom?
bang
body
butlast
capitalize
car
cdr
colon
comma
comment
comp
complement
concat
concat2
cond
cons
constantly
dec
def
defmacro
defn
doc
dotimes
downcase
drop
enumerate
error
errors
eval
even?
every
exclaim
exit
filter
flatten
foreach
forms
fuse
gensym
help
identity
if
if-not
inc
interpose
is
isqrt
juxt
lambda
last
len
let
let*
list
list*
list?
load
loop
macroexpand-1
map
mapcat
max
min
neg?
not
not=
nth
number?
odd?
or
partial
period
pos?
print
printl
println
progn
punctuate
punctuate-atom
quote
randalpha
randchoice
randigits
randint
range
readlist
reduce
rem
remove
repeat
repeatedly
reverse
screen-clear
screen-end
screen-get-key
screen-size
screen-start
screen-write
second
set!
shell
shuffle
sleep
some
sort
sort-by
source
split
swallow
syntax-quote
take
test
tosentence
true?
try
upcase
version
when
when-not
while
with-screen
zero?
Multiply 0 or more numbers
Type: native function
Arity: 0+
Args: (() . xs)
> (* 1 2 3)
;;=>
6
> (*)
;;=>
1
Exponentiation operator
Type: function
Arity: 2
Args: (n m)
> (** 1 0)
;;=>
1
> (** 2 4)
;;=>
16
> (** 10 10)
;;=>
10000000000
Add 0 or more numbers
Type: native function
Arity: 0+
Args: (() . xs)
> (+ 1 2 3)
;;=>
6
> (+)
;;=>
0
Subtract 0 or more numbers from the first argument
Type: native function
Arity: 1+
Args: (x . xs)
> (- 1 1)
;;=>
0
> (- 5 2 1)
;;=>
2
> (- 99)
;;=>
-99
Divide the first argument by the rest
Type: native function
Arity: 2+
Args: (numerator denominator1 . more)
> (/ 1 2)
;;=>
0
> (/ 12 2 3)
;;=>
2
> (/ 1 0)
;;=>
ERROR: ((builtin function /) (division by zero))
Return t if the arguments are in strictly increasing order, () otherwise
Type: native function
Arity: 1+
Args: (x . xs)
> (< 1 2)
;;=>
t
> (< 1 1)
;;=>
()
> (< 1)
;;=>
t
> (apply < (range 100))
;;=>
t
Return t if the arguments are in increasing or equal order, () otherwise
Type: native function
Arity: 1+
Args: (x . xs)
> (<= 1 2)
;;=>
t
> (<= 1 1)
;;=>
t
> (<= 1)
;;=>
t
Return t if the arguments are equal, () otherwise
Type: native function
Arity: 1+
Args: (x . xs)
> (= 1 1)
;;=>
t
> (= 1 2)
;;=>
()
> (apply = (repeat 10 t))
;;=>
t
Return t if the arguments are in strictly decreasing order, () otherwise
Type: native function
Arity: 1+
Args: (x . xs)
> (> 1 2)
;;=>
()
> (> 1 1)
;;=>
()
> (> 1)
;;=>
t
Return t if the arguments are in decreasing or equal order, () otherwise
Type: native function
Arity: 1+
Args: (x . xs)
> (>= 1 2)
;;=>
()
> (>= 1 1)
;;=>
t
Return absolute value of x
Type: function
Arity: 1
Args: (x)
> (abs 1)
;;=>
1
> (abs -100)
;;=>
100
Boolean and
Type: special form
Arity: 0+
Args: (() . xs)
(and)
;;=>
true
> (and t t)
;;=>
true
> (and t t ())
;;=>
()
> (and () (/ 1 0))
;;=>
()
Apply a function to a list of arguments
Type: native function
Arity: 2
Args: (f args)
> (apply + (repeat 10 1))
;;=>
10
> (apply * (cdr (range 10)))
;;=>
362880
Return t if the argument is an atom, () otherwise
Type: native function
Arity: 1
Args: (x)
> (atom? 1)
;;=>
()
> (atom? (quote one))
;;=>
t
Add an exclamation point at end of atom
Type: function
Arity: 1
Args: (a)
> (bang (quote Bang))
;;=>
Bang!
Return the body of a lambda function
Type: native function
Arity: 1
Args: (f)
> (body (lambda (x) (+ x 1)))
;;=>
((+ x 1))
Return everything but the last element
Type: function
Arity: 1
Args: (l)
> (butlast ())
;;=>
()
> (butlast (range 3))
;;=>
(0 1)
Return the atom argument, capitalized
Type: function
Arity: 1
Args: (a)
> (capitalize (quote hello))
;;=>
Hello
Return the first element of a list
Type: native function
Arity: 1
Args: (x)
> (car (quote (one two)))
;;=>
one
> (car ())
;;=>
()
Return a list with the first element removed
Type: native function
Arity: 1
Args: (x)
> (cdr (quote (one two)))
;;=>
(two)
> (cdr ())
;;=>
()
Add a colon at end of atom
Type: function
Arity: 1
Args: (a)
> (colon (quote remember-this))
;;=>
remember-this:
Add a comma at end of atom
Type: function
Arity: 1
Args: (a)
> (comma (quote hello))
;;=>
hello,
Ignore the expressions in the block
Type: macro
Arity: 0+
Args: (() . body)
> (comment twas brillig, and the slithy toves did gyre and gimble in the wabe)
;;=>
()
Function composition -- return a function which applies a series of functions in reverse order
Type: function
Arity: 0+
Args: (() . fs)
> ((comp) (quote hello))
;;=>
hello
> ((comp split) (quote hello))
;;=>
(h e l l o)
> ((comp len split) (quote hello))
;;=>
5
> ((comp (partial apply +) (partial map len) (partial map split)) (quote (hello world)))
;;=>
10
Return the logical complement of the supplied function
Type: function
Arity: 1
Args: (f)
> ((complement even?) 1)
;;=>
t
> (map (complement odd?) (range 5))
;;=>
(t () t () t)
Concatenenate any number of lists
Type: function
Arity: 0+
Args: (() . lists)
> (concat (range 3) (quote (wow)) (reverse (range 3)))
;;=>
(0 1 2 wow 2 1 0)
Concatenate two lists
Type: function
Arity: 2
Args: (a b)
> (concat2 () ())
;;=>
()
> (concat2 (quote (1 2)) (quote (3 4)))
;;=>
(1 2 3 4)
Fundamental branching construct
Type: special form
Arity: 0+
Args: (() . pairs)
> (cond)
;;=> ()
> (cond (t 1) (t 2) (t 3))
;;=> 1
> (cond (() 1) (t 2))
;;=> 2
Add an element to the front of a (possibly empty) list
Type: native function
Arity: 2
Args: (x xs)
> (cons 1 (quote (one two)))
;;=>
(1 one two)
> (cons 1 ())
;;=>
(1)
> (cons 1 2)
;;=>
(1 . 2)
Given a value, return a function which always returns that value
Type: function
Arity: 1
Args: (x)
> (map (constantly t) (range 10))
;;=>
(t t t t t t t t t t)
Return the supplied integer argument, minus one
Type: function
Arity: 1
Args: (n)
> (dec 2)
;;=>
1
> (dec -1)
;;=>
-2
Set a value
Type: special form
Arity: 2
Args: (name value)
> (def a 1)
;;=>
1
> a
;;=>
1
Create and name a macro
Type: special form
Arity: 2+
Args: (name args . body)
> (defmacro ignore-car (l)
(doc (ignore first element of list,
treat rest as normal expression)
(examples
(ignore-car (adorable + 1 2 3))
(ignore-car (deplorable - 4 4))))
(cdr l))
;;=>
()
> (ignore-car (hilarious * 2 3 4))
;;=>
24
Create and name a function
Type: special form
Arity: 2+
Args: (name args . body)
> (defn add (x y) (+ x y))
;;=>
()
> (add 1 2)
;;=>
3
> (defn add (x y)
(doc (add two numbers)
(examples
(add 1 2)))
(+ x y))
;;=>
()
> (doc add)
;;=>
((add two numbers) (examples (add 1 2)))
Return the doclist for a function
Type: native function
Arity: 1
Args: (x)
> (doc (lambda (x) (doc (does stuff) (and other stuff)) (+ x 1)))
;;=>
((does stuff) (and other stuff))
Execute body for each value in a list
Type: macro
Arity: 1+
Args: (n . body)
Return a new atom with all characters in lower case
Type: native function
Arity: 1
Args: (x)
> (downcase (quote Hello))
;;=>
hello
> (downcase (quote HELLO))
;;=>
hello
Drop n items from a list, then return the rest
Type: function
Arity: 2
Args: (n l)
> (drop 3 (range 10))
;;=>
(3 4 5 6 7 8 9)
Returning list of (i, x) pairs where i is the index (from zero) and x is the original element from l
Type: function
Arity: 1
Args: (l)
> (enumerate (quote (a b c)))
;;=>
((0 a) (1 b) (2 c))
Raise an error
Type: special form
Arity: 1
Args: (l)
> (defn ensure-list (x)
(when-not (list? x)
(error '(ensure-list argument not a list!))))
;;=>
()
> (ensure-list 3)
;;=>
ERROR in '(ensure-list 3)':
(ensure-list argument not a list!)
Error checking, for tests
Type: special form
Arity: 1+
Args: (expected . body)
> (errors '(is not a function)
(1))
;;=>
()
> (errors '(is not a function)
(+))
;;=>
ERROR in '(errors (quote (is not a function)) (+))':
error not found in ((quote (is not a function)) (+))
Evaluate an expression
Type: native function
Arity: 1
Args: (x)
> (eval (quote (one two)))
;;=>
ERROR: ((builtin function eval) (evaluating function object) (unknown symbol: one))
> (eval (quote (+ 1 2)))
;;=>
3
Return true if the supplied integer argument is even
Type: function
Arity: 1
Args: (n)
> (map even? (range 5))
;;=>
(t () t () t)
Return t if f applied to every element in l is truthy, else ()
Type: function
Arity: 2
Args: (f l)
> (every odd? (quote (1 3 5)))
;;=>
t
> (every odd? (quote (1 2 3 5)))
;;=>
()
Return l as a sentence... emphasized!
Type: function
Arity: 1
Args: (l)
> (exclaim (quote (well, hello)))
;;=>
(Well, hello!)
> (exclaim (quote (help)))
;;=>
(Help!)
> (exclaim (quote (begone, fiend)))
;;=>
(Begone, fiend!)
Exit the program
Type: native function
Arity: 0
Args: ()
Keep only values for which function f is true
Type: function
Arity: 2
Args: (f l)
> (filter odd? (range 5))
;;=>
(1 3)
Return a (possibly nested) list, flattened
Type: function
Arity: 1
Args: (l)
> (flatten (quote (this is a (really (nested) list))))
;;=>
(this is a really nested list)
Execute body for each value in a list
Type: macro
Arity: 2+
Args: (x xs . body)
Return available operators, as a list
Type: native function
Arity: 0
Args: ()
Fuse a list of numbers or atoms into a single atom
Type: native function
Arity: 1
Args: (x)
> (fuse (quote (A B C)))
;;=>
ABC
> (fuse (reverse (range 10)))
;;=>
9876543210
Return a new symbol
Type: native function
Arity: 0+
Args: (() . more)
Print a help message
Type: native function
Arity: 0
Args: ()
Return the argument
Type: function
Arity: 1
Args: (x)
Simple conditional with two branches
Type: macro
Arity: 3
Args: (condition then else)
> (if t 111 333)
;;=>
111
> (if () (quote abc) (quote def))
;;=>
def
Simple (inverted) conditional with two branches
Type: macro
Arity: 3
Args: (condition then else)
> (if-not (odd? 3) (quote (help, they broke three)) (quote (three is odd)))
;;=>
(three is odd)
Return the supplied integer argument, plus one
Type: function
Arity: 1
Args: (n)
Interpose x between all elements of l
Type: function
Arity: 2
Args: (x l)
> (interpose BANG (range 5))
;;=>
(0 ! 1 ! 2 ! 3 ! 4)
Assert a condition is truthy, or show failing code
Type: macro
Arity: 1
Args: (condition)
> (is t)
;;=>
()
> (is (car (cons () (quote (this one should fail)))))
;;=>
ERROR: ((assertion failed: (car (cons () (quote (this one should fail))))))
Integer square root
Type: native function
Arity: 1
Args: (x)
> (isqrt 4)
;;=>
2
> (isqrt 5)
;;=>
2
Create a function which combines multiple operations into a single list of results
Type: function
Arity: 0+
Args: (() . fs)
> ((juxt inc dec) 0)
;;=>
(1 -1)
> (map (juxt inc dec) (range 3))
;;=>
((1 -1) (2 0) (3 1))
> (map (juxt even? odd? zero?) (quote (-2 -1 0 1 2)))
;;=>
((t () ()) (() t ()) (t () t) (() t ()) (t () ()))
> (map (juxt) (range 3))
;;=>
(() () ())
Create a function
Type: special form
Arity: 1+
Args: (args . more)
> ((lambda () t))
;;=>
t
> ((lambda (x) (+ 5 x)) 5)
;;=>
10
> ((lambda my-length (x)
(if-not x
0
(+ 1 (my-length (cdr x)))))
(range 20))
;;=>
20
Return the last item in a list
Type: function
Arity: 1
Args: (l)
> (last (range 10))
;;=>
9
> (last (split (quote ATOM!)))
;;=>
!
Return the length of a list
Type: native function
Arity: 1
Args: (x)
> (len (range 10))
;;=>
10
Create a local scope with bindings
Type: special form
Arity: 1+
Args: (binding-pairs . body)
> (let ((a 1)
(b 2))
(+ a b))
;;=>
3
Let form with ability to refer to previously-bound pairs in the binding list
Type: macro
Arity: 1+
Args: (pairs . body)
> (let* ((a 1) (b (inc a))) (+ a b))
;;=>
3
Return a list of the given arguments
Type: native function
Arity: 0+
Args: (() . xs)
> (list 1 2 3)
;;=>
(1 2 3)
> (list)
;;=>
()
Create a list by consing everything but the last arg onto the last
Type: function
Arity: 0+
Args: (() . args)
> (list* 1 2 (quote (3)))
;;=>
(1 2 3)
> (list* 1 2 (quote (3 4)))
;;=>
(1 2 3 4)
> (list*)
;;=>
()
Return t if the argument is a list, () otherwise
Type: native function
Arity: 1
Args: (x)
> (list? (range 10))
;;=>
t
> (list? 1)
;;=>
()
Load and execute a file
Type: native function
Arity: 1
Args: (filename)
Loop forever
Type: special form
Arity: 1+
Args: ()
> (loop
(printl '(Help me, I am looping forever!))
(sleep 1000))
;; Prints =>
Help me, I am looping forever!
Help me, I am looping forever!
Help me, I am looping forever!
...
Expand a macro
Type: native function
Arity: 1
Args: (x)
> (macroexpand-1 (quote (+ x 1)))
;;=>
(+ x 1)
> (macroexpand-1 (quote (if () 1 2)))
;;=>
(cond (() 1) (t 2))
Apply the supplied function to every element in the supplied list
Type: function
Arity: 2
Args: (f l)
> (map odd? (range 5))
;;=>
(() t () t ())
> (map true? (quote (foo t () t 3)))
;;=>
(() t () t ())
Map a function onto a list and concatenate results
Type: function
Arity: 2
Args: (f l)
> (map list (range 5))
;;=>
((0) (1) (2) (3) (4))
> (mapcat list (range 5))
;;=>
(0 1 2 3 4)
> (map range (range 5))
;;=>
(() (0) (0 1) (0 1 2) (0 1 2 3))
> (mapcat range (range 5))
;;=>
(0 0 1 0 1 2 0 1 2 3)
Find maximum of one or more numbers
Type: function
Arity: 0+
Args: (() . args)
> (max -5)
;;=>
-5
> (max 2 3)
;;=>
3
> (apply max (range 10))
;;=>
9
Find minimum of one or more numbers
Type: function
Arity: 0+
Args: (() . args)
> (min -5)
;;=>
-5
> (min 2 3)
;;=>
2
> (apply min (range 10))
;;=>
0
Return true iff the supplied integer argument is less than zero
Type: function
Arity: 1
Args: (n)
> (map neg? (map (lambda (x) (- x 5)) (range 10)))
;;=>
(t t t t t () () () () ())
Return t if the argument is nil, () otherwise
Type: native function
Arity: 1
Args: (x)
> (not ())
;;=>
t
> (not t)
;;=>
()
> (not (range 10))
;;=>
()
Complement of = function
Type: function
Arity: 0+
Args: (() . terms)
> (not= 1 2)
;;=>
t
> (not= (quote a) (quote a))
;;=>
()
Find the nth value of a list, starting from zero
Type: function
Arity: 2
Args: (n l)
> (nth 3 (quote (one two three four five)))
;;=>
four
> (nth 1000 (range 2))
;;=>
()
Return true if the argument is a number, else ()
Type: native function
Arity: 1
Args: (x)
> (number? 1)
;;=>
t
> (number? t)
;;=>
()
> (number? +)
;;=>
()
Return true if the supplied integer argument is odd
Type: function
Arity: 1
Args: (n)
> (map even? (range 5))
;;=>
(t () t () t)
Boolean or
Type: special form
Arity: 0+
Args: (() . xs)
> (or)
;; => false
> (or t t)
;; => true
> (or t t ())
;; => t
Partial function application
Type: function
Arity: 1+
Args: (f . args)
> ((partial + 1) 1)
;;=>
2
> ((partial + 2 3) 4 5)
;;=>
14
Add a period at end of atom
Type: function
Arity: 1
Args: (a)
> (period (quote Woot))
;;=>
Woot.
Return true iff the supplied integer argument is greater than zero
Type: function
Arity: 1
Args: (n)
> (map pos? (map (lambda (x) (- x 5)) (range 10)))
;;=>
(() () () () () () t t t t)
Print the arguments
Type: native function
Arity: 0+
Args: (() . xs)
Print a list argument, without parentheses
Type: native function
Arity: 1
Args: (x)
Print the arguments and a newline
Type: native function
Arity: 0+
Args: (() . xs)
Execute multiple statements, returning the last
Type: macro
Arity: 0+
Args: (() . body)
> (progn)
;;=>
()
> (progn 1 2 3)
;;=>
3
Return x capitalized, with punctuation determined by the supplied function
Type: function
Arity: 2
Args: (f x)
Add a punctuation mark at end of atom
Type: function
Arity: 2
Args: (a mark)
> (punctuate-atom (quote list) (quote *))
;;=>
list*
> (punctuate-atom (quote list) COLON)
;;=>
list:
Quote an expression
Type: special form
Arity: 1
Args: (x)
> (quote foo)
foo
> (quote (1 2 3))
(1 2 3)
> '(1 2 3)
(1 2 3)
Return a list of random (English/Latin/unaccented) lower-case alphabetic characters
Type: function
Arity: 1
Args: (n)
Return an element at random from the supplied list
Type: function
Arity: 1
Args: (l)
Return a random integer between 0 and the argument minus 1
Type: function
Arity: 1
Args: (n)
Return a random integer between 0 and the argument minus 1
Type: native function
Arity: 1
Args: (x)
List of integers from 0 to n
Type: function
Arity: 1
Args: (n)
> (range 10)
;;=>
(0 1 2 3 4 5 6 7 8 9)
> (len (range 100))
;;=>
100
Read a list from stdin
Type: native function
Arity: 0
Args: ()
Successively apply a function against a list of arguments
Type: function
Arity: 2+
Args: (f x . args)
> (reduce * (cdr (range 10)))
;;=>
362880
> (reduce (lambda (acc x) (cons x acc)) () (range 10))
;;=>
(9 8 7 6 5 4 3 2 1 0)
Return remainder when second arg divides first
Type: native function
Arity: 2
Args: (x y)
> (rem 5 2)
;;=>
1
> (rem 4 2)
;;=>
0
> (rem 1 0)
;;=>
ERROR: ((builtin function rem) (division by zero))
Keep only values for which function f is false / the empty list
Type: function
Arity: 2
Args: (f l)
> (remove odd? (range 5))
;;=>
(0 2 4)
Return a list of length n whose elements are all x
Type: function
Arity: 2
Args: (n x)
> (repeat 5 (quote repetitive))
;;=>
(repetitive repetitive repetitive repetitive repetitive)
Return a list of length n whose elements are made from calling f repeatedly
Type: function
Arity: 2
Args: (n f)
> (repeatedly 3 (lambda () (range 5)))
;;=>
((0 1 2 3 4) (0 1 2 3 4) (0 1 2 3 4))
Reverse a list
Type: function
Arity: 1
Args: (l)
> (= (quote (c b a)) (reverse (quote (a b c))))
;;=>
t
Clear the screen
Type: native function
Arity: 0
Args: ()
Stop screen for text UIs, return to console mode
Type: native function
Arity: 0
Args: ()
Return a keystroke as an atom
Type: native function
Arity: 0
Args: ()
Return the screen size: width, height
Type: native function
Arity: 0
Args: ()
Start screen for text UIs
Type: native function
Arity: 0
Args: ()
Write a string to the screen
Type: native function
Arity: 3
Args: (x y list)
Return the second element of a list, or () if not enough elements
Type: function
Arity: 1
Args: (l)
> (second ())
;;=>
()
> (second (quote (a)))
;;=>
()
> (second (quote (a b)))
;;=>
b
> (second (quote (1 2 3)))
;;=>
2
Update a value in an existing binding
Type: special form
Arity: 2
Args: (name value)
> (def a 1)
;;=>
1
> a
;;=>
1
> (set! a 2)
;;=>
2
> a
;;=>
2
Run a shell subprocess, and return stdout, stderr, and exit code
Type: native function
Arity: 1
Args: (cmd)
Return a (quickly!) shuffled list
Type: native function
Arity: 1
Args: (xs)
Sleep for the given number of milliseconds
Type: native function
Arity: 1
Args: (ms)
Return f applied to first element for which that result is truthy, else ()
Type: function
Arity: 2
Args: (f l)
> (some even? (quote (1 3 5 7 9 11 13)))
;;=>
()
> (some even? (quote (1 3 5 7 9 1000 11 13)))
;;=>
t
Sort a list
Type: native function
Arity: 1
Args: (xs)
> (sort (quote (3 2 1)))
;;=>
(1 2 3)
> (sort (quote ()))
;;=>
()
> (sort (quote (c b a)))
;;=>
(a b c)
Sort a list by a function
Type: native function
Arity: 2
Args: (f xs)
> (sort-by first (quote ((3) (2) (1))))
;;=>
((1) (2) (3))
> (sort-by first (quote ()))
;;=>
()
> (sort-by second (quote ((quux 333) (zip 222) (afar 111))))
;;=>
((afar 111) (zip 222) (quux 333))
Show source for a function
Type: native function
Arity: 1
Args: (form)
> (source map)
;;=>
(lambda (f l) (when l (cons (f (car l)) (map f (cdr l)))))
> (source +)
;;=>
ERROR: ((builtin function source) (cannot get source of builtin function <builtin: +>))
Split an atom or number into a list of single-digit numbers or single-character atoms
Type: native function
Arity: 1
Args: (x)
> (split 123)
;;=>
(1 2 3)
> (split (quote abc))
;;=>
(a b c)
Swallow errors thrown in body, return t if any occur
Type: special form
Arity: 0+
Args: (() . body)
> (swallow
(error '(boom)))
;;=>
t
> (swallow 1 2 3)
;;=>
()
Syntax-quote an expression
Type: special form
Arity: 1
Args: (x)
> (syntax-quote foo)
foo
> (syntax-quote (1 2 3 4))
(1 2 3 4)
> (syntax-quote (1 (unquote (+ 1 1)) (splicing-unquote (list 3 4))))
(1 2 3 4)
> `(1 ~(+ 1 1) ~@(list 3 4))
(1 2 3 4)
Take up to n items from the supplied list
Type: function
Arity: 2
Args: (n l)
> (take 3 (range 10))
;;=>
(0 1 2)
Run tests
Type: special form
Arity: 0+
Args: (() . body)
Return l as a sentence... capitalized, with a period at the end
Type: function
Arity: 1
Args: (l)
> (tosentence (quote (to be, or not to be, that is the question)))
;;=>
(To be, or not to be, that is the question.)
Return t if the argument is t
Type: function
Arity: 1
Args: (x)
> (true? 3)
;;=>
()
> (true? t)
;;=>
t
Try to evaluate body, catch errors and handle them
Type: special form
Arity: 0+
Args: (() . body)
> (try (error '(boom)))
;;=>
ERROR:
((boom))
> (try
(error '(boom))
(catch e
(printl e)))
;;=>
(boom)
> (try (/ 1 0) (catch e (len e)))
2
>
Return the uppercase version of the given atom
Type: native function
Arity: 1
Args: (x)
> (upcase (quote abc))
;;=>
ABC
Return the version of the interpreter
Type: native function
Arity: 0
Args: ()
> (version)
;;=>
(0 0 57 dirty)
Simple conditional with single branch
Type: macro
Arity: 1+
Args: (condition . body)
> (when () (/ 1 0))
;;=>
()
> (when t (quote (the sun rises in the east)))
;;=>
(the sun rises in the east)
Complement of the when macro
Type: macro
Arity: 1+
Args: (condition . body)
> (when-not () (quote (do all the things)))
;;=>
(do all the things)
> (when-not t (error (quote (oh no mister bill))))
;;=>
()
Loop for as long as condition is true
Type: macro
Arity: 1+
Args: (condition . body)
> (while () (launch-missiles))
;;=>
()
Prepare for and clean up after screen operations
Type: macro
Arity: 0+
Args: (() . body)
Return true iff the supplied argument is zero
Type: function
Arity: 1
Args: (n)
> (zero? (quote zero))
;;=>
()
> (zero? (- 1 1))
;;=>
t