Skip to content

Latest commit

 

History

History
4405 lines (2723 loc) · 63.2 KB

l1.md

File metadata and controls

4405 lines (2723 loc) · 63.2 KB

The l1 Language

Jump to API / list of operators

The REPL

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

Expressions in l1 are atoms, lists, numbers, or functions:

Atoms

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

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

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

Boolean Logic

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

Special Characters

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

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)))))))

.!!!.???..
,??::?!,.?
?,?!?..:!!
,:.,?.:!!!
!!:?!::.,?
,:!!!:,!!:
,???:?!:!?
.,!!?,!:!?
!:,!!!.:!:
??.,,:.:..
()
>

Functions

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.

Flow of Control

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.

Assertions and Error Handling

is

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))
>

error

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))
>

errors

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))))
>

try ... catch

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.

swallow

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).

Subprocesses

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)

Macros

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))

Text User Interfaces

l1 has a few built-in functions for creating simple text UIs:

  • screen-clear: Clear the screen
  • screen-get-key: Get a keystroke
  • screen-write: Write a list, without parentheses, to an x and y 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: backspace
  • DEL: delete
  • DOWNARROW: down arrow
  • END: end key
  • LEFTARROW: left arrow
  • RIGHTARROW: right arrow
  • UPARROW: up arrow
  • ENTER: enter/return
  • ESC: 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...
          )))))

Loading Source Files

There are four ways of executing a source file, e.g. main.l1:

  1. (Unix/Linux/Mac only) Add a shebang at the beginning of the script;
  2. Pass main.l1 as an argument to l1: l1 main.l1, again at the shell prompt.
  3. Call the load function from the l1 REPL or from within another file: (load main.l1).
  4. "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.

Running l1 Programs as Command Line Scripts

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
$

Making Binary Executables

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.

Emacs Integration

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.

API Index

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?

Operators

*

Multiply 0 or more numbers

Type: native function

Arity: 0+

Args: (() . xs)

Examples

> (* 1 2 3)
;;=>
6
> (*)
;;=>
1

**

Exponentiation operator

Type: function

Arity: 2

Args: (n m)

Examples

> (** 1 0)
;;=>
1
> (** 2 4)
;;=>
16
> (** 10 10)
;;=>
10000000000

+

Add 0 or more numbers

Type: native function

Arity: 0+

Args: (() . xs)

Examples

> (+ 1 2 3)
;;=>
6
> (+)
;;=>
0

-

Subtract 0 or more numbers from the first argument

Type: native function

Arity: 1+

Args: (x . xs)

Examples

> (- 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)

Examples

> (/ 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)

Examples

> (< 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)

Examples

> (<= 1 2)
;;=>
t
> (<= 1 1)
;;=>
t
> (<= 1)
;;=>
t

=

Return t if the arguments are equal, () otherwise

Type: native function

Arity: 1+

Args: (x . xs)

Examples

> (= 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)

Examples

> (> 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)

Examples

> (>= 1 2)
;;=>
()
> (>= 1 1)
;;=>
t

abs

Return absolute value of x

Type: function

Arity: 1

Args: (x)

Examples

> (abs 1)
;;=>
1
> (abs -100)
;;=>
100

and

Boolean and

Type: special form

Arity: 0+

Args: (() . xs)

Examples

(and)
;;=>
true
> (and t t)
;;=>
true
> (and t t ())
;;=>
()
> (and () (/ 1 0))
;;=>
()

apply

Apply a function to a list of arguments

Type: native function

Arity: 2

Args: (f args)

Examples

> (apply + (repeat 10 1))
;;=>
10
> (apply * (cdr (range 10)))
;;=>
362880

atom?

Return t if the argument is an atom, () otherwise

Type: native function

Arity: 1

Args: (x)

Examples

> (atom? 1)
;;=>
()
> (atom? (quote one))
;;=>
t

bang

Add an exclamation point at end of atom

Type: function

Arity: 1

Args: (a)

Examples

> (bang (quote Bang))
;;=>
Bang!

body

Return the body of a lambda function

Type: native function

Arity: 1

Args: (f)

Examples

> (body (lambda (x) (+ x 1)))
;;=>
((+ x 1))

butlast

Return everything but the last element

Type: function

Arity: 1

Args: (l)

Examples

> (butlast ())
;;=>
()
> (butlast (range 3))
;;=>
(0 1)

capitalize

Return the atom argument, capitalized

Type: function

Arity: 1

Args: (a)

Examples

> (capitalize (quote hello))
;;=>
Hello

car

Return the first element of a list

Type: native function

Arity: 1

Args: (x)

Examples

> (car (quote (one two)))
;;=>
one
> (car ())
;;=>
()

cdr

Return a list with the first element removed

Type: native function

Arity: 1

Args: (x)

Examples

> (cdr (quote (one two)))
;;=>
(two)
> (cdr ())
;;=>
()

colon

Add a colon at end of atom

Type: function

Arity: 1

Args: (a)

Examples

> (colon (quote remember-this))
;;=>
remember-this:

comma

Add a comma at end of atom

Type: function

Arity: 1

Args: (a)

Examples

> (comma (quote hello))
;;=>
hello,

comment

Ignore the expressions in the block

Type: macro

Arity: 0+

Args: (() . body)

Examples

> (comment twas brillig, and the slithy toves did gyre and gimble in the wabe)
;;=>
()

comp

Function composition -- return a function which applies a series of functions in reverse order

Type: function

Arity: 0+

Args: (() . fs)

Examples

> ((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

complement

Return the logical complement of the supplied function

Type: function

Arity: 1

Args: (f)

Examples

> ((complement even?) 1)
;;=>
t
> (map (complement odd?) (range 5))
;;=>
(t () t () t)

concat

Concatenenate any number of lists

Type: function

Arity: 0+

Args: (() . lists)

Examples

> (concat (range 3) (quote (wow)) (reverse (range 3)))
;;=>
(0 1 2 wow 2 1 0)

concat2

Concatenate two lists

Type: function

Arity: 2

Args: (a b)

Examples

> (concat2 () ())
;;=>
()
> (concat2 (quote (1 2)) (quote (3 4)))
;;=>
(1 2 3 4)

cond

Fundamental branching construct

Type: special form

Arity: 0+

Args: (() . pairs)

Examples

> (cond)
;;=> ()
> (cond (t 1) (t 2) (t 3))
;;=> 1
> (cond (() 1) (t 2))
;;=> 2

cons

Add an element to the front of a (possibly empty) list

Type: native function

Arity: 2

Args: (x xs)

Examples

> (cons 1 (quote (one two)))
;;=>
(1 one two)
> (cons 1 ())
;;=>
(1)
> (cons 1 2)
;;=>
(1 . 2)

constantly

Given a value, return a function which always returns that value

Type: function

Arity: 1

Args: (x)

Examples

> (map (constantly t) (range 10))
;;=>
(t t t t t t t t t t)

dec

Return the supplied integer argument, minus one

Type: function

Arity: 1

Args: (n)

Examples

> (dec 2)
;;=>
1
> (dec -1)
;;=>
-2

def

Set a value

Type: special form

Arity: 2

Args: (name value)

Examples

> (def a 1)
;;=>
1
> a
;;=>
1

defmacro

Create and name a macro

Type: special form

Arity: 2+

Args: (name args . body)

Examples

> (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
	

defn

Create and name a function

Type: special form

Arity: 2+

Args: (name args . body)

Examples

> (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)))

doc

Return the doclist for a function

Type: native function

Arity: 1

Args: (x)

Examples

> (doc (lambda (x) (doc (does stuff) (and other stuff)) (+ x 1)))
;;=>
((does stuff) (and other stuff))

dotimes

Execute body for each value in a list

Type: macro

Arity: 1+

Args: (n . body)

downcase

Return a new atom with all characters in lower case

Type: native function

Arity: 1

Args: (x)

Examples

> (downcase (quote Hello))
;;=>
hello
> (downcase (quote HELLO))
;;=>
hello

drop

Drop n items from a list, then return the rest

Type: function

Arity: 2

Args: (n l)

Examples

> (drop 3 (range 10))
;;=>
(3 4 5 6 7 8 9)

enumerate

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)

Examples

> (enumerate (quote (a b c)))
;;=>
((0 a) (1 b) (2 c))

error

Raise an error

Type: special form

Arity: 1

Args: (l)

Examples

> (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!)

errors

Error checking, for tests

Type: special form

Arity: 1+

Args: (expected . body)

Examples

> (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)) (+))

eval

Evaluate an expression

Type: native function

Arity: 1

Args: (x)

Examples

> (eval (quote (one two)))
;;=>
ERROR: ((builtin function eval) (evaluating function object) (unknown symbol: one))
> (eval (quote (+ 1 2)))
;;=>
3

even?

Return true if the supplied integer argument is even

Type: function

Arity: 1

Args: (n)

Examples

> (map even? (range 5))
;;=>
(t () t () t)

every

Return t if f applied to every element in l is truthy, else ()

Type: function

Arity: 2

Args: (f l)

Examples

> (every odd? (quote (1 3 5)))
;;=>
t
> (every odd? (quote (1 2 3 5)))
;;=>
()

exclaim

Return l as a sentence... emphasized!

Type: function

Arity: 1

Args: (l)

Examples

> (exclaim (quote (well, hello)))
;;=>
(Well, hello!)
> (exclaim (quote (help)))
;;=>
(Help!)
> (exclaim (quote (begone, fiend)))
;;=>
(Begone, fiend!)

exit

Exit the program

Type: native function

Arity: 0

Args: ()

filter

Keep only values for which function f is true

Type: function

Arity: 2

Args: (f l)

Examples

> (filter odd? (range 5))
;;=>
(1 3)

flatten

Return a (possibly nested) list, flattened

Type: function

Arity: 1

Args: (l)

Examples

> (flatten (quote (this is a (really (nested) list))))
;;=>
(this is a really nested list)

foreach

Execute body for each value in a list

Type: macro

Arity: 2+

Args: (x xs . body)

forms

Return available operators, as a list

Type: native function

Arity: 0

Args: ()

fuse

Fuse a list of numbers or atoms into a single atom

Type: native function

Arity: 1

Args: (x)

Examples

> (fuse (quote (A B C)))
;;=>
ABC
> (fuse (reverse (range 10)))
;;=>
9876543210

gensym

Return a new symbol

Type: native function

Arity: 0+

Args: (() . more)

help

Print a help message

Type: native function

Arity: 0

Args: ()

identity

Return the argument

Type: function

Arity: 1

Args: (x)

if

Simple conditional with two branches

Type: macro

Arity: 3

Args: (condition then else)

Examples

> (if t 111 333)
;;=>
111
> (if () (quote abc) (quote def))
;;=>
def

if-not

Simple (inverted) conditional with two branches

Type: macro

Arity: 3

Args: (condition then else)

Examples

> (if-not (odd? 3) (quote (help, they broke three)) (quote (three is odd)))
;;=>
(three is odd)

inc

Return the supplied integer argument, plus one

Type: function

Arity: 1

Args: (n)

interpose

Interpose x between all elements of l

Type: function

Arity: 2

Args: (x l)

Examples

> (interpose BANG (range 5))
;;=>
(0 ! 1 ! 2 ! 3 ! 4)

is

Assert a condition is truthy, or show failing code

Type: macro

Arity: 1

Args: (condition)

Examples

> (is t)
;;=>
()
> (is (car (cons () (quote (this one should fail)))))
;;=>
ERROR: ((assertion failed: (car (cons () (quote (this one should fail))))))

isqrt

Integer square root

Type: native function

Arity: 1

Args: (x)

Examples

> (isqrt 4)
;;=>
2
> (isqrt 5)
;;=>
2

juxt

Create a function which combines multiple operations into a single list of results

Type: function

Arity: 0+

Args: (() . fs)

Examples

> ((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))
;;=>
(() () ())

lambda

Create a function

Type: special form

Arity: 1+

Args: (args . more)

Examples

> ((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

last

Return the last item in a list

Type: function

Arity: 1

Args: (l)

Examples

> (last (range 10))
;;=>
9
> (last (split (quote ATOM!)))
;;=>
!

len

Return the length of a list

Type: native function

Arity: 1

Args: (x)

Examples

> (len (range 10))
;;=>
10

let

Create a local scope with bindings

Type: special form

Arity: 1+

Args: (binding-pairs . body)

Examples

> (let ((a 1)
        (b 2))
    (+ a b))
;;=>
3

let*

Let form with ability to refer to previously-bound pairs in the binding list

Type: macro

Arity: 1+

Args: (pairs . body)

Examples

> (let* ((a 1) (b (inc a))) (+ a b))
;;=>
3

list

Return a list of the given arguments

Type: native function

Arity: 0+

Args: (() . xs)

Examples

> (list 1 2 3)
;;=>
(1 2 3)
> (list)
;;=>
()

list*

Create a list by consing everything but the last arg onto the last

Type: function

Arity: 0+

Args: (() . args)

Examples

> (list* 1 2 (quote (3)))
;;=>
(1 2 3)
> (list* 1 2 (quote (3 4)))
;;=>
(1 2 3 4)
> (list*)
;;=>
()

list?

Return t if the argument is a list, () otherwise

Type: native function

Arity: 1

Args: (x)

Examples

> (list? (range 10))
;;=>
t
> (list? 1)
;;=>
()

load

Load and execute a file

Type: native function

Arity: 1

Args: (filename)

loop

Loop forever

Type: special form

Arity: 1+

Args: ()

Examples

> (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!
...

macroexpand-1

Expand a macro

Type: native function

Arity: 1

Args: (x)

Examples

> (macroexpand-1 (quote (+ x 1)))
;;=>
(+ x 1)
> (macroexpand-1 (quote (if () 1 2)))
;;=>
(cond (() 1) (t 2))

map

Apply the supplied function to every element in the supplied list

Type: function

Arity: 2

Args: (f l)

Examples

> (map odd? (range 5))
;;=>
(() t () t ())
> (map true? (quote (foo t () t 3)))
;;=>
(() t () t ())

mapcat

Map a function onto a list and concatenate results

Type: function

Arity: 2

Args: (f l)

Examples

> (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)

max

Find maximum of one or more numbers

Type: function

Arity: 0+

Args: (() . args)

Examples

> (max -5)
;;=>
-5
> (max 2 3)
;;=>
3
> (apply max (range 10))
;;=>
9

min

Find minimum of one or more numbers

Type: function

Arity: 0+

Args: (() . args)

Examples

> (min -5)
;;=>
-5
> (min 2 3)
;;=>
2
> (apply min (range 10))
;;=>
0

neg?

Return true iff the supplied integer argument is less than zero

Type: function

Arity: 1

Args: (n)

Examples

> (map neg? (map (lambda (x) (- x 5)) (range 10)))
;;=>
(t t t t t () () () () ())

not

Return t if the argument is nil, () otherwise

Type: native function

Arity: 1

Args: (x)

Examples

> (not ())
;;=>
t
> (not t)
;;=>
()
> (not (range 10))
;;=>
()

not=

Complement of = function

Type: function

Arity: 0+

Args: (() . terms)

Examples

> (not= 1 2)
;;=>
t
> (not= (quote a) (quote a))
;;=>
()

nth

Find the nth value of a list, starting from zero

Type: function

Arity: 2

Args: (n l)

Examples

> (nth 3 (quote (one two three four five)))
;;=>
four
> (nth 1000 (range 2))
;;=>
()

number?

Return true if the argument is a number, else ()

Type: native function

Arity: 1

Args: (x)

Examples

> (number? 1)
;;=>
t
> (number? t)
;;=>
()
> (number? +)
;;=>
()

odd?

Return true if the supplied integer argument is odd

Type: function

Arity: 1

Args: (n)

Examples

> (map even? (range 5))
;;=>
(t () t () t)

or

Boolean or

Type: special form

Arity: 0+

Args: (() . xs)

Examples

> (or)
;; => false
> (or t t)
;; => true
> (or t t ())
;; => t

partial

Partial function application

Type: function

Arity: 1+

Args: (f . args)

Examples

> ((partial + 1) 1)
;;=>
2
> ((partial + 2 3) 4 5)
;;=>
14

period

Add a period at end of atom

Type: function

Arity: 1

Args: (a)

Examples

> (period (quote Woot))
;;=>
Woot.

pos?

Return true iff the supplied integer argument is greater than zero

Type: function

Arity: 1

Args: (n)

Examples

> (map pos? (map (lambda (x) (- x 5)) (range 10)))
;;=>
(() () () () () () t t t t)

print

Print the arguments

Type: native function

Arity: 0+

Args: (() . xs)

printl

Print a list argument, without parentheses

Type: native function

Arity: 1

Args: (x)

println

Print the arguments and a newline

Type: native function

Arity: 0+

Args: (() . xs)

progn

Execute multiple statements, returning the last

Type: macro

Arity: 0+

Args: (() . body)

Examples

> (progn)
;;=>
()
> (progn 1 2 3)
;;=>
3

punctuate

Return x capitalized, with punctuation determined by the supplied function

Type: function

Arity: 2

Args: (f x)

punctuate-atom

Add a punctuation mark at end of atom

Type: function

Arity: 2

Args: (a mark)

Examples

> (punctuate-atom (quote list) (quote *))
;;=>
list*
> (punctuate-atom (quote list) COLON)
;;=>
list:

quote

Quote an expression

Type: special form

Arity: 1

Args: (x)

Examples

> (quote foo)
foo
> (quote (1 2 3))
(1 2 3)
> '(1 2 3)
(1 2 3)

randalpha

Return a list of random (English/Latin/unaccented) lower-case alphabetic characters

Type: function

Arity: 1

Args: (n)

randchoice

Return an element at random from the supplied list

Type: function

Arity: 1

Args: (l)

randigits

Return a random integer between 0 and the argument minus 1

Type: function

Arity: 1

Args: (n)

randint

Return a random integer between 0 and the argument minus 1

Type: native function

Arity: 1

Args: (x)

range

List of integers from 0 to n

Type: function

Arity: 1

Args: (n)

Examples

> (range 10)
;;=>
(0 1 2 3 4 5 6 7 8 9)
> (len (range 100))
;;=>
100

readlist

Read a list from stdin

Type: native function

Arity: 0

Args: ()

reduce

Successively apply a function against a list of arguments

Type: function

Arity: 2+

Args: (f x . args)

Examples

> (reduce * (cdr (range 10)))
;;=>
362880
> (reduce (lambda (acc x) (cons x acc)) () (range 10))
;;=>
(9 8 7 6 5 4 3 2 1 0)

rem

Return remainder when second arg divides first

Type: native function

Arity: 2

Args: (x y)

Examples

> (rem 5 2)
;;=>
1
> (rem 4 2)
;;=>
0
> (rem 1 0)
;;=>
ERROR: ((builtin function rem) (division by zero))

remove

Keep only values for which function f is false / the empty list

Type: function

Arity: 2

Args: (f l)

Examples

> (remove odd? (range 5))
;;=>
(0 2 4)

repeat

Return a list of length n whose elements are all x

Type: function

Arity: 2

Args: (n x)

Examples

> (repeat 5 (quote repetitive))
;;=>
(repetitive repetitive repetitive repetitive repetitive)

repeatedly

Return a list of length n whose elements are made from calling f repeatedly

Type: function

Arity: 2

Args: (n f)

Examples

> (repeatedly 3 (lambda () (range 5)))
;;=>
((0 1 2 3 4) (0 1 2 3 4) (0 1 2 3 4))

reverse

Reverse a list

Type: function

Arity: 1

Args: (l)

Examples

> (= (quote (c b a)) (reverse (quote (a b c))))
;;=>
t

screen-clear

Clear the screen

Type: native function

Arity: 0

Args: ()

screen-end

Stop screen for text UIs, return to console mode

Type: native function

Arity: 0

Args: ()

screen-get-key

Return a keystroke as an atom

Type: native function

Arity: 0

Args: ()

screen-size

Return the screen size: width, height

Type: native function

Arity: 0

Args: ()

screen-start

Start screen for text UIs

Type: native function

Arity: 0

Args: ()

screen-write

Write a string to the screen

Type: native function

Arity: 3

Args: (x y list)

second

Return the second element of a list, or () if not enough elements

Type: function

Arity: 1

Args: (l)

Examples

> (second ())
;;=>
()
> (second (quote (a)))
;;=>
()
> (second (quote (a b)))
;;=>
b
> (second (quote (1 2 3)))
;;=>
2

set!

Update a value in an existing binding

Type: special form

Arity: 2

Args: (name value)

Examples

> (def a 1)
;;=>
1
> a
;;=>
1
> (set! a 2)
;;=>
2
> a
;;=>
2

shell

Run a shell subprocess, and return stdout, stderr, and exit code

Type: native function

Arity: 1

Args: (cmd)

shuffle

Return a (quickly!) shuffled list

Type: native function

Arity: 1

Args: (xs)

sleep

Sleep for the given number of milliseconds

Type: native function

Arity: 1

Args: (ms)

some

Return f applied to first element for which that result is truthy, else ()

Type: function

Arity: 2

Args: (f l)

Examples

> (some even? (quote (1 3 5 7 9 11 13)))
;;=>
()
> (some even? (quote (1 3 5 7 9 1000 11 13)))
;;=>
t

sort

Sort a list

Type: native function

Arity: 1

Args: (xs)

Examples

> (sort (quote (3 2 1)))
;;=>
(1 2 3)
> (sort (quote ()))
;;=>
()
> (sort (quote (c b a)))
;;=>
(a b c)

sort-by

Sort a list by a function

Type: native function

Arity: 2

Args: (f xs)

Examples

> (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))

source

Show source for a function

Type: native function

Arity: 1

Args: (form)

Examples

> (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

Split an atom or number into a list of single-digit numbers or single-character atoms

Type: native function

Arity: 1

Args: (x)

Examples

> (split 123)
;;=>
(1 2 3)
> (split (quote abc))
;;=>
(a b c)

swallow

Swallow errors thrown in body, return t if any occur

Type: special form

Arity: 0+

Args: (() . body)

Examples

> (swallow
	(error '(boom)))
;;=>
t
> (swallow 1 2 3)
;;=>
()

syntax-quote

Syntax-quote an expression

Type: special form

Arity: 1

Args: (x)

Examples

> (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

Take up to n items from the supplied list

Type: function

Arity: 2

Args: (n l)

Examples

> (take 3 (range 10))
;;=>
(0 1 2)

test

Run tests

Type: special form

Arity: 0+

Args: (() . body)

tosentence

Return l as a sentence... capitalized, with a period at the end

Type: function

Arity: 1

Args: (l)

Examples

> (tosentence (quote (to be, or not to be, that is the question)))
;;=>
(To be, or not to be, that is the question.)

true?

Return t if the argument is t

Type: function

Arity: 1

Args: (x)

Examples

> (true? 3)
;;=>
()
> (true? t)
;;=>
t

try

Try to evaluate body, catch errors and handle them

Type: special form

Arity: 0+

Args: (() . body)

Examples

> (try (error '(boom)))
;;=>
ERROR:
((boom))
> (try
    (error '(boom))
    (catch e
      (printl e)))
;;=>
(boom)
> (try (/ 1 0) (catch e (len e)))
2
>

upcase

Return the uppercase version of the given atom

Type: native function

Arity: 1

Args: (x)

Examples

> (upcase (quote abc))
;;=>
ABC

version

Return the version of the interpreter

Type: native function

Arity: 0

Args: ()

Examples

> (version)
;;=>
(0 0 56 dirty)

when

Simple conditional with single branch

Type: macro

Arity: 1+

Args: (condition . body)

Examples

> (when () (/ 1 0))
;;=>
()
> (when t (quote (the sun rises in the east)))
;;=>
(the sun rises in the east)

when-not

Complement of the when macro

Type: macro

Arity: 1+

Args: (condition . body)

Examples

> (when-not () (quote (do all the things)))
;;=>
(do all the things)
> (when-not t (error (quote (oh no mister bill))))
;;=>
()

while

Loop for as long as condition is true

Type: macro

Arity: 1+

Args: (condition . body)

Examples

> (while () (launch-missiles))
;;=>
()

with-screen

Prepare for and clean up after screen operations

Type: macro

Arity: 0+

Args: (() . body)

zero?

Return true iff the supplied argument is zero

Type: function

Arity: 1

Args: (n)

Examples

> (zero? (quote zero))
;;=>
()
> (zero? (- 1 1))
;;=>
t