Language Reference v1.0

Data types

Cons Cells are the central data type in classic Lisp used both as dotted pairs (a . b) and general lists (a b). For an overview of cons cells and how to use them see [1], [3], or http://cs.gmu.edu/~sean/lisp/cons/. Note that dotted pairs require spaces around the period; (a.b) is a list containing the symbol a.b, not a cons cell with car of a and cdr of b.

Symbols are simple identifiers, e.g. function-name. Symbols follow the follow 3 simple rules:

  • can only contain graphic characters (i.e. no control characters)
  • can not contain any of the characters: ();,"’` []{}
  • can not contain whitespace

Typically, - is used to separate words in a symbol, _ is used in special symbols (such as system use) to separate words and as a prefix and suffix. The characters ?, !, and * are typically used as the final character of a function name to denote:

? a predicate, e.g. nil?

! a mutating function (changes the argument rather than returning a modified copy), e.g. set-car!

* a variant of the primary function, e.g. flatten (which does a one level flattening of a list) and flatten* (which is a recursive flatten)

If a symbol ends with : it is what is called a naked symbol. It has no value other than itself. If it is evaluated, the result is the symbol itself. This feature is utilized by frames.

Strings are any sequence of characters other than " enclosed by a pair of ", e.g. "string". If you need to have " in a string, use \".

Integers are sixtyfour bit signed integers. Both decimal and hexadecimal formats are supported. E.g. 26, 0x1a, 0x1A. As of V1.0, the proper Scheme syntax for hex numbers is supported. E.g.: #x1a. As well, Scheme binary numbers are supported now. E.g. #b1001001001.

Floats are Go float32 numbers. Accordingly they are signed. All arithmetic functions with the exception of modulus work as expected for both integers and floats. Numbers are coerced to floats as required, specifically if any arguments are float, all are converted to float and the result will be a float.

Booleans represent true and false. nil, (), and false are all logically false, everything else is logically true. Boolean literals are #t and #f for true and false, respectively.

Functions are user defined procedures. They are covered in detail later.

Objects allow you to encapsulate a Go object (struct) in a Lisp data object. There is no way to do this from Lisp itself, but is useful when writing primitive functions (see below).

Ports provide access to files for reading and writing.

Bytearrays are simply objects that encapsulate []byte objects. The difference is that there is syntactic support for them. Use square braces surrounding a list of numbers between 0 and 255, inclusive. For example: [1 2 3 4 5]. That format will parse to an object containing a the Go bytearray (i.e. []byte). Bytearrays evaluate to themselves. There are also functions for doing bytearray manipulation.

Frames are sets of named slots that hold arbitrary values, including functions. Frames can inherit from other frames in a prototypical manner. Frames are inspired by the languages Self [4] and Newtonscript [5].

Primitives are just as they are in Lisp or Smalltalk: functions written in Go and exposed as functions in Lisp. The combinaion of primitives and objects allow you to integrate with the underlying Go program. Special forms are almost identical to primitives, except that they use normal evaluation order instead of applicative order which functions and primitives use.

Control Structures

(quote sexpr)

sexpr

Suppresses evaluation of the expression.

(quote (+ 1 2)) ⇒ (+ 1 2)

There is a shortcut for quote that uses the single quote:

'(+ 1 2)         ⇒ (+ 1 2)

(cond (predicate sexpr…)…)

Each predicate is evaluated in order until one results in true value. The expressions associated with this predicate are then evaluated in order, and the result of the cond is the result of the last evaluation. If all predicates evaluate to false values, the value of cond in indeterminate. If, however, the final predicate is the symbol else the expressions associated with it are evaluated, and the value of cond is the value of the last evaluation.

(cond ((> 3 2) 'greater)
      ((< 3 2) 'less)) ⇒ greater

(cond ((> 3 3) 'greater)
      ((< 3 3) 'less)
      (else 'equal))   ⇒ equal

(case target ((value…) sexpr…)… [(else sexpr…)])

case chooses code to evaluate based on the value of evaluating target. Any number of expressions can be associated with each target value. None of the value expressions are evaluated.

(define (test-func x)
  (case x
    ((0) "zero")
    ((1) "one")
    ((2 3) "some")
    (else "unknown")))

(test-func 0) ⇒ "zero"
(test-func 1) ⇒ "one"
(test-func 2) ⇒ "some"
(test-func 3) ⇒ "some"
(test-func 5) ⇒ "unknown"

Note that if a value expression contains a symbol(s) they do not need to be quoted.

(case name
  ((a b) 1)
  ((c d) 2))

(if condition true-clause)

This is a special case of cond that is more familiar to programmers used to Algol style languages. if has two forms, one that conditionally evaluates a single sexpr (see below about begin which provides a way to use multiple sexprs in this context).

Iff condition evaluates to logically true, true-clause is evaluated and the result of that is the result of the if form, otherwise nil is the result of the form.

(if (< 1 2)
    "less") ⇒ "less"
(if (< 2 2)
    "less") ⇒ nil

(if condition true-clause false-clause)

If condition evaluates to logically true, true-clause is evaluated and the result of that is the result of the if form, otherwise the false-clause is evaluated and is the result of the form.

(if (< 1 2)
    "less"
    "not less") ⇒ "less"
(if (< 2 2)
    "less"
    "not less") ⇒ "not less"

(when condition sexpr…)

If condition evaluates to logically true, the sequence of sexpr is evaluated and the result of the last one is the result of the when form, otherwise nil is the result.

(when (> x 5)
      (write-line "greater")
      (+ x 2))

The above is equivalent to the following, but is simpler and clearer.

(if (> x 5)
    (begin (write-line "greater")
           (+ x 2)))

(unless condition sexpr…)

If condition evaluates to logically false, the sequence of sexpr is evaluated and the result of the last one is the result of the unless form, otherwise nil is the result.

(unless (> x 5)
        (write-line "greater")
        (+ x 2))

The above is equivalent to the following, but is simpler and clearer.

(if (> x 5)
    ()
    (begin (write-line "greater")
           (+ x 2)))

(define symbol value)

Evaluates the value expression and binds it to the symbol, returning the value.

(define x (+ 2 3)) ⇒ 5
x ⇒ 5

(begin sexpr…)

Evaluates the each sexpr in order, returning the result of the last one evaluated. Used in a context that allows a single sexpr but you need multiple. E.g.

(if (predicate)
    (begin
      (do-something)
      (do-something-else)))

(let ((name value)…) sexpr…)

Create a local scope and bindings for evaluating a body of code. The first argument is a list of bindings. Each binding is a raw symbol (doesn’t get evaluated) that is the name to be bound, and a value (which is evaluated). These bindings are added to a scope that is local to the let. The body is evaluated in this local scope. The value of the let is the last evaluation result. E.g.

(let ((x 1)
      (y 2))
     (+ x y)) ⇒ 3

Bindings DO NOT cascade. I.e. each binding’s value is evaluated in the context of the scope where the let appears. E.g.

(define x 39)
(let ((x 1)
      (y (+ x 2)))
  (+ x y)) ⇒ 42

When the value of y is computed, x is taken from the outside environment, and there it is bound to 39, so y is bound to 41. When the addition is evaluated in the body of the let, the let-bound value of x (which is 1) is used, so the result is 42.

As mentioned above, let takes a sequence of sexprs as it’s body. In effect the body of the let is an implicit begin.

(let* ((name value)…) sexpr…)

NOTE: this used to be let and there was no let* In many cases this won’t matter, but in some it will. You might want to check any existing use of let.

This is the same as let EXCEPT that bindings do cascade. I.e. each binding’s value is evaluated in the context of the local scope. E.g.

(let ((x 1)
      (y (+ x 2)))
  (+ x y)) ⇒ 4

(letrec ((name value)…) sexpr…)

The variables are bound to fresh locations holding unassigned values, the inits are evaluated in the extended environment (in some unspecified order), each variable is assigned to the result of the corresponding init, the expressions are evaluated sequentially in the extended environment, and the value of the last expression is returned. Each binding of a variable has the entire letrec expression as its region, making it possible to define mutually recursive procedures.

GoLisp allows any of the inits to be omitted, in which case the corresponding variables are unassigned.

(letrec ((even? (lambda (n)
                  (if (zero? n)
                      #t
                      (odd? (- n 1)))))
         (odd? (lambda (n)
                 (if (zero? n)
                     #f
                     (even? (- n 1))))))
  (even? 88))

One restriction on letrec is very important: it shall be possible to evaluated each init without assigning or referring to the value of any variable. If this restriction is violated, then it is an error. The restriction is necessary because Scheme passes arguments by value rather than by name. In the most common uses of letrec, all the inits are lambda expressions and the restriction is satisfied automatically.

(let name ((name value)…) sexpr…)

MIT/GNU Scheme permits a variant on the syntax of let called named let which provides a more general looping construct than do, and may also be used to express recursions. GoLisp also supports this syntax.

Named let has the same syntax and semantics as ordinary let except that name is bound within the expressions to a procedure whose formal arguments are the variables and whose body is the expressions. Thus the execution of the expressions may be repeated by invoking the procedure named by name.

Here is an example:

(let loop
     ((numbers '(3 -2 1 6 -5))
      (nonneg '())
      (neg '()))
  (cond ((null? numbers)
         (list nonneg neg))
        ((>= (car numbers) 0)
         (loop (cdr numbers)
               (cons (car numbers) nonneg)
               neg))
        (else
         (loop (cdr numbers)
               nonneg
               (cons (car numbers) neg)))))
 ⇒ ((6 1 3) (-5 -2))

(do ((name initial next)…) (test [sexpr…]) [sexpr…])

This is a general purpose iteration construct. There are three sections:

bindings This is similar to let in that it defines names that can be used in the remainder of the scope of the do. Like let it is a list of lists, each starting with the binding name followed by the initial value of the binding. The difference is that this is followed by an expression that is evaluated at the beginning of each subsequent pass through the loop, and whose result is used as a new binding of the name.

termination This is a list whose first element is an expression which is evaluated before each pass through the loop (after rebinding the variables). If it evaluates to a truthy value, the remaining expressions are evaluated in turn. Looping ends and the result of the final evaluation is used as the value of the do form.

body This is a sequence of expressions that are evaluated in order each time though the loop. This section can be empty.

Here’s an example of using do to iteratively find the length of a list.

(do ((l some-list (cdr l))
     (count 0 (+ 1 count)))
    ((nil? l) count))

(-> value sexpr|symbol…)

This creates a function chain. value (evaluated first) is used as the first argument to the first sexpr. The result of each sexpr is used as the first argument of the next, and the result of the final sexpr is the value of the -> form. If a sexpr would take a single argument (which would be provided by the value or the result of the previous sexpr, just the function name can be used.

The form (-> 0 a b c) is equivalent to (c (b (a 0))).

(-> 1 (+ 3) (- 2))     ⇒ 2     ; (- (+ 1 3) 2)
(-> 1 (+ 3) (- 2) str) ⇒ "2"   ; (str (- (+ 1 3) 2))

(=> value sexpr|symbol…)

This operates similarly to -> with two differences:

  1. value (evaluated once at the beginning) is used as the initial argument to each function, and they are independent and do not pass results one to another.

  2. value is the result of the form.

The expression

(=> 1 a b c)

is equivalent to

(begin
  (a 1)
  (b 1)
  (c 1)
  1)

and

(=> (+ x 1) a b c)

is the same as

(let ((y (+ x 1)))
  (a y)
  (b y)
  (c y)
  y)

Assignment

(set! name new-value)

The way to assign (i.e. rebind) a symbol. name is the symbol to be rebound. new-value is evaluated to arrive at the new value to be bound to. Use of set! is frowned upon, and should not be used without thought. See Chapter 3 of [1] for a discussion of the pros and cons of assignment.

(define x 5)
(set! x 10)
x ⇒ 10

Assigning takes action in the closest scope in which the symbol is bound. In the following example, that’s the global scope, so when the let exits, x still has the rebound value of 10.

(define x 5)
(let ()
  (set! x 10)
  x) ⇒ 10
x    ⇒ 10

In this example, x is bound by the let, i.e. in the local context of the let, so the rebinding is done in the scope of the let and doesn’t extend beyond it.

(define x 5)
(let ((x 1))
  (set! x 10)
  x) ⇒ 10
x    ⇒ 5

(set-car! cons-cell new-value)

Set the car pointer of cons-cell.

(define a '(1 2))
(set-car! a 3)
a ⇒ (3 2)

(set-cdr! cons-cell new-value)

Set the cdr pointer of cons-cell.

(define a '(1 2))
(set-cdr! a 3)
a ⇒ (1 . 3)

(set-nth! list n new-value)

Set the car pointer of the nth cons cell of list. Numbering starts at 1.

(define a '(1 2 3 4))
(set-nth! a 3 0)
a ⇒ (1 2 0 4)

Functions

(lambda (param…) expr…)

Creates an anonymous function. This can then be used in a function call.

((lambda (x)
   (+ x x))
 5) ⇒ 10

lambda creates a local environment at the point of it’s evaluation. This environment is attached to the resulting function, and any binding or symbol lookup starts in this local environment. For example, consider the following code (from [1], §3.2.2):

(define square (lambda (x)
   (* x x)))

(define sum-of-squares (lambda (x y)
  (+ (square x) (square y))))

(define f (lambda (a)
  (sum-of-squares (+ a 1) (* a 2))))

Each of the three functions have their own associated definition environment pointer that points back to the global environment.

When a function is executed, the lambda creates a new environment frame for the evaluation that (in this case) points back to the global environment. This scoping approach is what is described in §3 of [1].

![Fig. 1. Procedure objects in the global frame.](images/function_declarations.png) ![Fig. 2. Environments created by evaluating (f 5) using the procedures in Fig 1.] (images/function_invocations.png)

Functions can also be named (i.e. bound to a symbol) by a special form of define:

(define (symbol param…) body)

symbol specifies the name (how you reference the function)

param>… parameters of the function, these are bound to the respective arguments when the function is called.

body the sequence of expressions that are evaluated in order when the function is called. The final evaluation result becomes the value of evaluation of the function.

(define (square x)
  (* x x))

(define (sum-of-squares x y)
  (+ (square x) (square y)))

(define (f a)
  (sum-of-squares (+ a 1) (* a 2)))

(apply function sexpr…)

Apply the function that results from evaluating function to the argument list resulting from evaluating each sexpr.

Each initial sexpr can evaluate to any type of object, but the final one (and there must be at least one sexpr) must evaluate to a list.

(apply + 1 2 '(3 4)) ⇒ 10
(apply + '(1 2 3 4)) ⇒ 10

(eval sexpr)

Evaluate sexpr.

(eval '(+ 1 2 3 4)) ⇒ 10

(definition-of function)

Fetch the definition of function. This returns an expression that can be evaluated to define it. One use of this is to copy definitions to a source file.

(define (square x)
  (* x x))

(definition-of square) ⇒ (define (square x) (* x x))

(define square (lambda (x)
                 (* x x)))

(definition-of square) ⇒ (define square (lambda (x) (* x x)))

Macros

Macros are for making syntactic extensions to Lisp.

(quasiquote sexpr)

`sexpr

This defines a template expression that can be filled in by unquote and unquote-splicing.

(unquote sexpr)

,sexpr

This evaluates sexpr and inserts the result into the template in place of the unquote expression.

(unquote-splicing sexpr)

,@sexpr

This evaluates sexpr and inserts the result, which is assumed to be a list, into the template in place of the unquote-splicing expression. The list itself is not inserted, rather the sequence of items in the list are.

`(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b) ⇒ (a 3 4 5 6 b)

Quasiquote forms may be nested. Substitutions are made only for unquoted components appearing at the same nesting level as the outermost quasiquote. The nesting level increases by one inside each successive quasiquotation, and decreases by one inside each unquotation.

‘(a ‘(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f) ⇒ (a ‘(b ,(+ 1 2) ,(foo 4 d) e) f)

(let ((name1 ’x)
      (name2 ’y))
     ‘(a ‘(b ,,name1 ,’,name2 d) e)) ⇒ (a ‘(b ,x ,’y d) e)

(defmacro (symbol [param…]) sexpr)

Create a named macro:

symbol specifies the name (how you reference the macro)

param… parameters of the macro, these are bound to the respective arguments when the macro is invoked. NOTE that the arguments to a macro invocation are not evaluated, but are passed as is to the macro to do with as it wishes.

sexpr the template expression that is processed when the macro is invoked. The result of evaluating the processed template expression becomes the value of the macro’s invocation.

(defmacro (double x)
   `(+ ,x ,x))
(double 5) ⇒ 10

(expand symbol [sexpr…])

Expands the macro named by symbol, passing the evaluated sequence of sexpr as arguments. NOTE: whereas invoking the macro (in the same way you invoke a function) expands and evaluates, expand (as you would expect) only expands the macro, resulting in the expanded template sexpr. This can then be evaluated as desired.

(defmacro (double x)
   `(+ ,x ,x))
(expand double 5) ⇒ (+ 5 5)

Builtin functions

Type checking

(atom? sexpr)

Returns whether sexpr is an atom, i.e. a number, string, boolean, or symbol.

(list? sexpr)

Returns whether sexpr is a list.

(pair? sexpr)

Returns whether sexpr is a pair, i.e. a list or a dotted pair.

(alist? sexpr)

Returns whether sexpr is an association list.

(nil? sexpr)

(null? sexpr)

Returns whether sexpr is an empty list, i.e. the nil value.

(notnil? sexpr)

(notnull? sexpr)

Returns whether sexpr is not an empty list, i.e. the nil value.

(symbol? sexpr)

Returns whether sexpr is a symbol.

(string? sexpr)

Returns whether sexpr is a string.

(number? sexpr)

Returns whether sexpr is a number.

(integer? sexpr)

Returns whether sexpr is an integer.

(float? sexpr)

Returns whether sexpr is a floating point number.

(function? sexpr)

Returns whether sexpr is a function.

(macro? sexpr)

Returns whether sexpr is a macro.

(frame? sexpr)

Returns whether sexpr is a frame.

(bytearray? sexpr)

Returns whether sexpr is a bytearray.

(port? sexpr)

Returns whether sexpr is a port.

Arithmetic

(+ number…)

Adds a series of numbers, e.g.

(+ 4)        ⇒ 4
(+ 4 2)      ⇒ 6
(+ 4 2 7)    ⇒ 13
(+ 1.2 2.3)  ⇒ 3.5
(+ -1.2 2.3) ⇒ 1.0999999

(- number…)

Sequentially subtracts a sequence of numbers, e.g.

(- 10 3)     ⇒ 7
(- 10 3 4)   ⇒ 3
(- 10 3 4 5) ⇒ -2
(- 2.3 1.2)  ⇒ 1.0999999
(- 1.2 2.3)  ⇒ -1.0999999

(* number…)

Multiplies a series of numbers, e.g.

(* 4)       ⇒ 4
(* 4 2)     ⇒ 8
(* 4 2 7)   ⇒ 56
(* 1.2 2.3) ⇒ 2.76

(/ number…)

(quotient number…)

Sequentially divides a sequence of numbers. E.g.

(/ 30 2)    ⇒ 15
(/ 30 2 3 ) ⇒ 5
(/ 30 4)    ⇒ 7
(/ 30.0 4)  ⇒ 7.5

(% numerator denominator)

(modulo numerator denominator)

Returns the remainder of division of two numbers. NOTE: modulus only works for integers. E.g.

(% 4 2) ⇒ 0
(% 4 3) ⇒ 1

(floor number)

Returns the greatest integer value less than or equal to number. number can be an integer or float. Return value is a float.

(floor 3.4) ⇒ 3.0
(floor -3.4) ⇒ -4.0
(floor 3) ⇒ 3.0

(ceiling number)

Returns the largest integer value greater than or equal to number. number can be an integer or float. Return value is a float.

(ceiling 3.4) ⇒ 4.0
(ceiling -3.4) ⇒ -3.0
(ceiling 3) ⇒ 3.0

(min list)

Returns the smallest value in list, which has to be a list of numeric values.

(min '(1 2 3)) ⇒ 1

(max list)

Returns the largest value in list, which has to be a list of numeric values.

(max '(1 2 3)) ⇒ 3

(succ integer)

Returns the successor to integer. I.e. the one after.

(succ -2) ⇒ -1 (succ 2) ⇒ 3

(pred integer)

Returns the predessor to integer. I.e. the one before.

(pred -2) ⇒ -3 (pred 2) ⇒ 1

(integer number)

Returns the integer value of number. If it is an integer, it is simply returned. However, if it is a float the integer part is returned. Note that this is implemented in go by casting the float32 to a int64 so converting negative floats gives a large integer.

(integer 5)    ⇒ 5
(integer 5.2)  ⇒ 5
(integer 5.8)  ⇒ 5
(integer -1.0) ⇒ 4294967295

(float number)

Returns the float value of number. If it is a float, it is simply returned. However, if it is an integer the corresponding float is returned.

(float 5) ⇒ 5.0

Note that converting a float to a string for printing using the format %g to use the minimum number of characters so 5.0 will actually print as 5.

(number->string number [base])

Converts number (first converted to an integer) to a string, in the given base. Allowed bases are 2, 8, 10, and 16. If the base is omitted, 10 is used. No base prefixes (e.g. 0x for base 16) are added.

(number->string 42)    ⇒ "42"
(number->string 42 2)  ⇒ "101010"
(number->string 42 8)  ⇒ "52"
(number->string 42 10) ⇒ "42"
(number->string 42 16) ⇒ "2a"
(number->string 42 15) ⇒ "Unsupported base: 15"

(string->number numeric-string [base])

Converts numeric-string to an integer, in the given base. Allowed bases are 2, 8, 10, and 16. If the base is omitted, 10 is used. No base prefixes (e.g. 0x for base 16) are allowed. Specifying an unsupported base will result in 0.

(string->number "42")       ⇒ 42
(string->number "101010" 2) ⇒ 42
(string->number "52" 8)     ⇒ 42
(string->number "42" 10)    ⇒ 42
(string->number "2a" 16)    ⇒ 42
(string->number "42" 15)    ⇒ 0

(str sexpr…)

Creates a string from concatenating the string forms of all the _sexpr_s.

  (str 1)        ⇒ "1"
  (str 'symbol)  ⇒ "symbol"
  (str '(1 2 3)) ⇒ "(1 2 3)"

Comparisons

All comparison operations work with floating point numbers as well.

(< number number)

Returns whether the first argument is less than the second argument.

(< 1 2) ⇒ #t
(< 2 1) ⇒ #f
(< 2 2) ⇒ #f

(> number number)

Returns whether the first argument is greater than the second argument.

(> 1 2) ⇒ #f
(> 2 1) ⇒ #t
(> 2 2) ⇒ #f

(== number number)

(eq? sexpr sexpr)

(eqv? sexpr sexpr)

(equal? sexpr sexpr)

Returns whether the first argument is equal to the second argument.

(== 1 2) ⇒ #f
(== 1 1) ⇒ #t

(!= number number)

(neq? sexpr sexpr)

Returns whether the first argument is not equal to the second argument.

(!= 1 2) ⇒ #t
(!= 1 1) ⇒ #f

(<= number number)

Returns whether the first argument is less than or equal to the second argument.

(<= 1 2) ⇒ #t
(<= 2 1) ⇒ #f
(<= 2 2) ⇒ #t

(>= number number)

Returns whether the first argument is greater than or equal to the second argument.

(>= 1 2) ⇒ #f
(>= 2 1) ⇒ #t
(>= 2 2) ⇒ #t

(odd? number)

Returns whether the argument (which must be an integer) is odd.

(odd? 2) ⇒ #f
(odd? 5) ⇒ #t

(even? number)

Returns whether the argument (which must be an integer) is even.

(even? 2) ⇒ #t
(even? 5) ⇒ #f

(zero? number)

Returns whether the argument is zero.

(zero? 1) ⇒ #f
(zero? 0) ⇒ #t
(zero? -1) ⇒ #f

(positive? number)

Returns whether the argument is positive.

(positive? 1) ⇒ #t
(positive? 0) ⇒ #f
(positive? -1) ⇒ #f

(negative? number)

Returns whether the argument is negative.

(negative? 1) ⇒ #f
(negative? 0) ⇒ #f
(negative? -1) ⇒ #t

Logical

(not sexpr)

Returns whether the boolean negation of the argument.

(! #t) ⇒ #f
(! #f) ⇒ #t

(and sexpr…)

Computes a logical and of the evaluated arguments, with shortcircuiting. I.e. each argument is evaluated in order until one evaluates to false or the end of the list is reached. If all evaluate to true the result of the and is true, otherwise it is result of evaluating the final argument. If an argument evaluates to false, subsequent arguments are not evaluated. and with no arguments evaluates to true.

(or sexpr…)

Computes a logical or of the evaluated arguments, with shortcircuiting. I.e. each argument is evaluated in order until one evaluates to true or the end of the list is reached. If all evaluate to false the result of the or is false, otherwise it is the result of evaluating the final argument. If an argument evaluates to true, subsequent arguments are not evaluated. or with no arguments evaluates to false.

Binary

(binary-and int-1 int-2)

Performs a bitwise AND of int-1 and int-2, returning the result.

(number->string (binary-and 0xaa 0x0f) 16) ⇒ "a"
(number->string (binary-and 0xaa 0xf0) 16) ⇒ "a0"

(binary-or int-1 int-2)

Performs a bitwise OR of int-1 and int-2, returning the result.

(number->string (binary-or 0xaa 0x0f) 16) ⇒ "af"
(number->string (binary-or 0xaa 0xf0) 16) ⇒ "fa"

(binary-not int)

Performs a bitwise NOT of int, returning the result.

(number->string (binary-not 0x000000aa) 16) ⇒ "ffffff55"

(left-shift int count)

Shifts int left by count bits, returning the result.

(number->string (left-shift (string->number "10101010" 2) 1) 2) ⇒ "101010100"
(number->string (left-shift (string->number "10101010" 2) 3) 2) ⇒ "10101010000"

(right-shift int count)

Shifts int right by count bits, returning the result.

(number->string (right-shift (string->number "10000" 2) 1) 2) ⇒ "1000"
(number->string (right-shift (string->number "10000" 2) 4) 2) ⇒ "1"

Lists

(cons sexpr1 sexpr2)

Creates a pair from sexpr1 and sexpr2. If sexpr2 is a list, then the effect of cons is to return sexpr2 prefixed by sexpr1.

(cons 1 2)      ⇒ (1 . 2)
(cons 1 '(2 3)) ⇒ (1 2 3)

(list sexpr…)

Creates a list out of a sequence of values.

(list 1 2 3)                   ⇒ (1 2 3)
(list (+ 1 1) (+ 2 2) (+ 3 3)) ⇒ (2 4 6)
(list 'a 'b '(c d))            ⇒ (a b (c d))

(make-list k [sexpr])

Returns a newly allocated list of length k, whose elements are all sexpr. If elemen_sexpr_ is not supplied, it defaults to the empty list.

(make-list 4 ’c) ⇒ (c c c c)

(cons* sexpr…)

cons* is similar to list, except that cons* conses together the last two arguments rather than consing the last argument with the empty list. If the last argument is not a list the result is an improper list. If the last argument is a list, the result is a list consisting of the initial arguments and all of the items in the final argument. If there is only one argument, the result is the argument.

(cons* 'a 'b 'c)     ⇒ (a b . c)
(cons* 'a 'b '(c d)) ⇒ (a b c d)  ; compare to list above
(cons* 'a)           ⇒ a
(cons* '(a b))       ⇒ (a b)

(append sexpr…)

Successively append the result of evaluating each argument. This makes a copy of each argument, appending it to the resulting list. Arguments need not be lists.

(append '(1 2 3) '(4 5 6)) ⇒ (1 2 3 4 5 6)
(append '(1 2 3) 4)        ⇒ (1 2 3 4)
(append 1 '(2 3 4) 5 6)    ⇒ (1 2 3 4 5 6)

(append! list sexpr)

Append the result of evaluating sexpr to list. This appends to list, and returns it. The side effect (indicated by the !) is that list is modified, unlike append. For example:

(define a '(1 2 3))
(append a '(4 5 6))  ⇒ (1 2 3 4 5 6)
a                    ⇒ (1 2 3)

(append! a '(4 5 6)) ⇒ (1 2 3 4 5 6)
a                    ⇒ (1 2 3 4 5 6)

(length list)

Return the number of items in list. Nested lists are a single thing.

(length '())          ⇒ 0
(length '(1 2 3))     ⇒ 3
(length '(1 (2 3) 4)) ⇒ 3

(interval hi)

Creates a list of numbers from 1 to hi, inclusive. hi must be a positive integer.

(interval 5) ⇒ (1 2 3 4 5)
(interval 2) ⇒ (1 2)

(interval lo hi)

Creates a list of numbers from lo to hi, inclusive, stepping by 1. If lo > hi, a step of -1 is used.

(interval 1 5) ⇒ (1 2 3 4 5)
(interval -2 2) ⇒ (-2 -1 0 1 2)
(interval 5 1) ⇒ (5 4 3 2 1)
(interval 2 -2) ⇒ (2 1 0 -1 -2)

(interval lo hi step)

Creates a list of numbers from lo to hi, inclusive (if possible), step apart. step must be non-zero and it’s sign must match the ordering of lo and hi. I.e. if lo > hi, step must be negative, otherwise positive.

(interval 1 5 2) ⇒ (1 3 5)
(interval 1 8 2) ⇒ (1 3 5 7)
(interval -2 2 2) ⇒ (-2 0 2)
(interval 2 -2 -2) ⇒ (2 0 -2)
(interval 5 1 -2) ⇒ (5 3 1)
(interval -1 -8 -2) ⇒ (-1 -3 -5 -7)

(car list)

(head list)

Returns the first item in the list, i.e. the car pointer of the first cell in the chain referenced by list.

(car nil)                    ⇒ nil
(car '(a))                   ⇒ a
(car '(a b c))               ⇒ a
(car (list (+ 1 1) (+ 2 2))) ⇒ 2

(cdr list)

(rest list)

(tail list)

Returns the rest of the list, i.e. the cdr pointer of the first cell in the chain referenced by list. rest is provided as an alias.

(cdr nil)                    ⇒ nil
(cdr '(a))                   ⇒ nil
(cdr '(a b c))               ⇒ (b c)
(cdr (list (+ 1 1) (+ 2 2))) ⇒ (4)

The above car and cdr functions are the fundamental list decomposition operations. There are more that are based on these two. First there are a series of functions explicitly using car and cdr such as caddr which is equivalent to (car (cdr (cdr list))). Up to cddddr is supported.

(caddr '(1 2 3 4 5)) ⇒ 3
(cdddr '(1 2 3 4 5)) ⇒ (4 5)

(general-car-cdr list path)

This is a generalization of car and cdr. The argument path encodes a particular sequence of car and cdr operations, which general-car-cdr executes on list. path is an non-negative integer that encodes the operations in a bitwise fashion: a zero bit represents a cdr operation, and a one bit represents a car. The bits are executed LSB to MSB, and the most significant one bit, rather than being interpreted as an operation, signals the end of the sequence.

For example, the following are equivalent:

(general-car-cdr object #b1011)
(cdr (car (car object)))

Here is a partial table of path/operation equivalents:

#b10 cdr
#b11 car
#b100 cddr
#b101 cdar
#b110 cadr
#b111 caar
#b1000 cdddr

(first list)

(second list)

(third list)

(fourth list)

(fifth list)

(sixth list)

(seventh list)

(eighth list)

(ninth list)

(tenth list)

These do exactly what you’d think: return the first, second, etc. item from list.

(nth list n)

Returns the nth element of list, the first being at n = 1.

(nth '(1 2 3 4) 1) ⇒ 1
(nth '(1 2 3 4) 3) ⇒ 3

(list-ref list k)

Similar to nth, but is the proper Scheme implementation: Returns the kth element of list, the first being at k = 0.

(list-ref '(1 2 3 4) 0) ⇒ 1
(list-ref '(1 2 3 4) 3) ⇒ 4

(take k list)

Returns a newly allocated list consisting of the first k elements of list. K must not be greater than the length of list.

(take 3 '(1 2 3 4 5))      ⇒ '(1 2 3)

(drop k list)

Returns the sublist of list obtained by omitting the first k elements. The result, if it is not the empty list, shares structure with list. K must not be greater than the length of list.

(drop 3 '(1 2 3 4 5))      ⇒ '(4 5)

The following two functions are functionally identical to take and drop, respecively, but use the correct Scheme syntax and argument order.

(list-head list k)

Returns a newly allocated list consisting of the first k elements of list. K must not be greater than the length of list.

(list-head '(1 2 3 4 5) 3)      ⇒ '(1 2 3)

(list-tail list k)

Returns the sublist of list obtained by omitting the first k elements. The result, if it is not the empty list, shares structure with list. K must not be greater than the length of list.

(list-tail '(1 2 3 4 5) 3)      ⇒ '(4 5)

(sublist list start end)

Start and end must be integers satisfying

0 <= start <= end <= (length list)

sublist returns a newly allocated list formed from the elements of list beginning at index start (inclusive) and ending at end (exclusive).

(memq object list)

(memv object list)

(member object list)

Returns the first pair of list whose car is object; the returned pair is always one from which list is composed. If object does not occur in list, #f (n.b.: not the empty list) is returned.

(memp predicate list)

Returns the first pair of list for which predicate returns #t when passed the car; the returned pair is always one from which list is composed. If predicate never returns #t, #f (n.b.: not the empty list) is returned.

(find predicate list)

Returns the first element in list for which predicate is true; returns #f if it doesn’t find such an element. Predicate must be a procedure of one argument.

 (find even? '(3 1 4 1 5 9)) ⇒ 4

(find-tail predicate list)

Returns the first pair of list whose car satisfies predicate; returns #f if there’s no such pair.

 (find-tail even? '(3 1 4 1 5 9)) ⇒ (4 1 5 9)

(map function list…)

Applies function (which has to take the same number of arguments as there are list arguments to map) to the corresponding elements of each list argument in order, returning the list of the results.

(map - '(1 2 3 4)))                   ⇒ (-1 -2 -3 -4)

(map (lambda (x)
             (* x x))
     '(1 2 3 4))                      ⇒ (1 4 9 16)

(map + '(1 2 3) '(4 5 6)))            ⇒ (5 7 9)

(map list '(1 2 3) '(4 5 6) '(7 8 9)) ⇒ ((1 4 7) (2 5 8) (3 6 9))

The list argument do not need to be the same length, but map will only iterate a number of times equal to the length of the shortest. So

(map list '(1 2) '(4 5 6) '(7 8 9 10))  ⇒ ((1 4 7) (2 5 8))

since the shortest list argument contains two elements. Thus map iterates twice (over the first two sets of values) creating a result list with two elements in it.

(for-each function list…)

Applies function (which has to take the same number of arguments as there are list arguments to map) to the corresponding elements of each list argument in order. In this it is identical to map; the difference is that for-each returns nil. This is handy when you want to map over a list (or lists) for the side effect of the operation on each element (or group of elements), without regard for the results.

(reduce-left function initial list)

(reduce function initial list)

Combines all the elements of list using the binary operation function. For example, using + one can add up all the elements:

(reduce-left + 0 list-of-numbers)

The argument initial is used only if list is empty; in this case initial is the result of the call to reduce-left. If list has a single item, that item is returned. Otherwise, the arguments are reduced in a left-associative fashion. For example:

(reduce-left + 0 '(1 2 3 4))       ⇒ 10
(reduce-left + 0 '(1 2))           ⇒ 3
(reduce-left + 0 '(1))             ⇒ 1
(reduce-left + 0 '())              ⇒ 0
(reduce-left list '() '(1 2 3 4))  ⇒ (((1 2) 3) 4)

reduce is an alias of reduce-left, kept in for legacy support.

(reduce-right function initial list)

Like reduce-left except that it is right-associative.

(reduce-right list '() '(1 2 3 4)) ⇒ (1 (2 (3 4)))

(fold-right function initial list)

Combines all of the elements of list using the binary operation procedure. Unlike reduce-left and reduce-right, initial is always used:

(fold-right + 0 '(1 2 3 4))      ⇒ 10
(fold-right list '() '(1 2 3 4)) ⇒ (1 (2 (3 (4 ()))))

Fold-right has interesting properties because it establishes a homomorphism between (cons, ()) and (procedure, initial). It can be thought of as replacing the pairs in the spine of the list with procedure and replacing the () at the end with initial. Many of the classical list-processing procedures can be expressed in terms of fold-right, at least for the simple versions that take a fixed number of arguments:

(define (copy-list list)
  (fold-right cons '() list))

(define (append list1 list2)
  (fold-right cons list2 list1))

(define (map p list)
  (fold-right (lambda (x r) (cons (p x) r)) '() list))

(define (reverse items)
  (fold-right (lambda (x r) (append r (list x))) '() items))

(fold-left function initial list)

Combines all the elements of list using the binary operation procedure. Elements are combined starting with initial and then the elements of list from left to right. Whereas fold-right is recursive in nature, capturing the essence of cdr-ing down a list and then computing a result, fold-left is iterative in nature, combining the elements as the list is traversed.

(fold-left list '() '(1 2 3 4)) ⇒ ((((() 1) 2) 3) 4)

(define (length list)
  (fold-left (lambda (sum element) (+ sum 1)) 0 list))

(define (reverse items)
  (fold-left (lambda (x y) (cons y x)) () items))

(filter function list)

Returns a list containing only elements from the list argument for which function returns true.

(filter odd? '(1 2 3 4 5)) ⇒ (1 3 5)

(remove function list)

Returns a list containing only elements from the list argument for which function returns false.

(remove odd? '(1 2 3 4 5)) ⇒ (2 4)

(partition integer list)

Returns a list of lists. Each nested list in the result is integer items long, except the final one, which may be shorter.

(partition 3 '(1 2 3 4 5 6 7 8)) ⇒ ((1 2 3) (4 5 6) (7 8))

(partition predicate list)

Returns a list of 2 lists. The first is all elements of list for which predicate returns #t, and the second os the elements for which it returns #f.

(partition even? '(1 2 3 4 5 6 7 8)) ⇒ ((2 4 6 8) (1 3 5 7))

(flatten list)

Returns a list with the contents of all top level nested lists placed directly in the result. This is best illustrated with some examples:

(flatten '(a b c d)) ⇒ (a b c d)
(flatten '(a (b c) d)) ⇒ (a b c d)
(flatten '(a (b (c d)))) ⇒ (a b (c d))

(flatten* list)

Returns a list with the contents of all nested lists placed directly in the result. This is also best illustrated with some examples:

(flatten* '(a b c d)) ⇒ (a b c d)
(flatten* '(a (b c) d)) ⇒ (a b c d)
(flatten* '(a (b (c d)))) ⇒ (a b c d)

(sort list predicate)

predicate must be a procedure of two arguments that defines a total ordering on the elements of list. In other words, if x and y are two distinct elements of ‘list’, then it must be the case that

(and (procedure x y) (procedure y x)) ⇒ #f

sort returns a newly allocated list whose elements are those of list, except that they are rearranged to be sorted in the order defined by predicate. So, for example, if the elements of list are numbers, and procedure is <, then the resulting elements are sorted in monotonically nondecreasing order. Likewise, if procedure is >, the resulting elements are sorted in monotonically nonincreasing order. To be precise, if ‘x’ and ‘y’ are any two adjacent elements in the result, where ‘x’ precedes ‘y’, it is the case that

(procedure y x) ⇒ #f

Set-like functions

(union list…)

Returns a list that contains all items in the argument lists. Each item appears only once in the result regardless of whether it was repeated in any list.

(union '(1 2 3) '(4 5))      ⇒ (1 2 3 4 5)
(union '(1 2 3) '(3 4 5))    ⇒ (1 2 3 4 5)
(union '(1 2 3 2) '(4 4 5))  ⇒ (1 2 3 4 5)

(intersection list…)

Returns a list that contains only items that are in all list arguments.

(intersection '(1 2 3) '(3 4 5)) ⇒ (3)
(intersection '() '(3 4 5))      ⇒ ()

(complement list…)

Returns a list that contains only items that were in the first list argument, but not in any of the subsequent argument lists.

(complement '(1 2 3 4 5) '(1 3 5))      ⇒ (2 4)
(complement '() '(1 2))                 ⇒ ()
(complement '(1 2 3 4 5) '(1 2) '(3 4)) ⇒ (5)

Association lists

Association lists (aka a-lists) are a classic Lisp way to handle associating a value with a key. Implementation is a list of dotted pairs: the car is the key, and the cdr is the value.

(acons key value [a-list])

This returns the result of adding a pair (key . value) to a-list. key, value, and a-list are evaluated. If a-list is omitted, it defaults to nil. The new pair is placed at the head of a-list (if provided).

(acons 'a 1)                    ⇒ ((a . 1))
(acons 'a 1 '((b . 2) (c . 3))) ⇒ ((a . 1) (b . 2) (c . 3)))

(pairlis keys values [a-list])

Creates an a-list from lists of keys and values. A third argument can be provided which is an exicting a-list that the new pairs will be added to.

(pairlis '(a b) '(1 2))                    ⇒ ((b . 2) (a . 1)))
(pairlis '(a b) '(1 2) '((c . 3) (d . 4))) ⇒ ((b . 2) (a . 1) (c . 3) (d . 4))))

Now that we have a way to create a-lists, we have a couple functions to get data out of them.

(assoc key a-list)

Return the pair from a-list whose car is equal to key. The empty pair is returned is key isn’t found.

(assoc 'a '((a . 1) (b . 2) (c . 3))) ⇒ (a . 1)
(assoc 'c '((a . 1) (b . 2)))         ⇒ ()

(rassoc value a-list)

Return the pair from a-list whose cdr is equal to value. The empty pair is returned is value isn’t found.

(rassoc '1 '((a . 1) (b . 2) (c . 3))) ⇒ (a . 1)
(rassoc '3 '((a . 1) (b . 2)))         ⇒ ()

(dissoc value a-list)

Remove the pair from a-list whose car is equal to value. The resulting alist is returned.

(dissoc 'a '((a . 1) (b . 2))) ⇒ ((b . 2))
(dissoc 'b '((a . 1) (b . 2))) ⇒ ((a . 1))

Bytearrays

(list-to-bytearray list of bytes and/or bytearrays)

(list->bytearray list of bytes and/or bytearrays)

The list must be comprised of elements that are either numbers between 0 and 255, inclusive, or existing bytearray objects. The result is an object containing a []byte.

(list-to-bytearray '(1 2 3 4))     ⇒ [1 2 3 4]
(list-to-bytearray '(1 [2 3] 4))   ⇒ [1 2 3 4]
(list-to-bytearray '([1 2] [3 4])) ⇒ [1 2 3 4]

(bytearray-to-list bytearray)

(bytearray->list bytearray)

This is the opposite of the previous function. The result is a list containing the numbers in the bytearray.

(bytearray-to-list [1 2 3 4]) ⇒ (1 2 3 4)

(replace-byte bytearray index value)

Makes a copy of bytearray and replaces the byte at index with value. The new bytearray with the replaced byte is returned. index must be a valid index into the byte array (zero based), and value must be a valid byte value, i.e. between 0 and 255, inclusive.

(define a [1 2 3 4])    ⇒ [1 2 3 4]
(replace-byte a 2 100)  ⇒ [1 2 100 4]
a                       ⇒ [1 2 3 4]

(replace-byte! bytearray index value)

Replaces the byte at index with value. index must be a valid index into the byte array (zero based), and value must be a valid byte value, i.e. between 0 and 255, inclusive. The original byte array is modified and the returned bytearray object is the one that is passed to the function.

(define a [1 2 3 4])    ⇒ [1 2 3 4]
(replace-byte! a 2 100) ⇒ [1 2 100 4]
a                       ⇒ [1 2 100 4]

(extract-byte bytearray index)

Fetch and return the byte at index. index must be a valid index into the byte array (zero based).

(extract-byte [1 2 3 4] 2) ⇒ 3

(append-bytes bytearray byte…)

(append-bytes bytearray list of bytes)

(append-bytes bytearray bytearray…)

Appends the rest of the arguments to a copy of the bytearray that is the first arg. The copy is returned. Things that can be appended are: a single byte, a sequence of bytes (as a sequence of separate arguments), a list of bytes, a bytearray object, a sequence of bytearray objects (as a sequence of separate arguments), and code that evaluates to a byte, list of bytes, or bytearray.

(append-bytes [1 2 3] 4)            ⇒ [1 2 3 4]
(append-bytes [1 2 3] 4 5 6)        ⇒ [1 2 3 4 5 6]
(append-bytes [1 2 3] '(4 5 6))     ⇒ [1 2 3 4 5 6]
(append-bytes [1 2 3] [4 5 6])      ⇒ [1 2 3 4 5 6]
(append-bytes [1 2 3] [4 5] [6])    ⇒ [1 2 3 4 5 6]
(append-bytes [1 2 3] (list 4 5 6)) ⇒ [1 2 3 4 5 6]

(append-bytes! bytearray byte…)

(append-bytes! bytearray list of bytes)

(append-bytes! bytearray bytearray…)

As with append-bytes, but modifies and returns the bytearray that is passed in rather than making a copy.

(define a [1 2 3])  ⇒ [1 2 3]
(append-bytes a 4)  ⇒ [1 2 3 4]
a                   ⇒ [1 2 3]
(append-bytes! a 4) ⇒ [1 2 3 4]
a                   ⇒ [1 2 3 4]

(take k bytearray)

As with the list implementation of take, fetches and returns a new bytearray consisting of the bytes from bytearray starting at index k.

(take 1 [1 2 3] ⇒ [1])
(take 3 [1 2 3] ⇒ [1 2 3])

(drop k bytearray)

(drop 1 [1 2 3] ⇒ [2 3])
(drop 2 [1 2 3] ⇒ [3])

As with the list implementation of drop, fetches and returns a new bytearray consisting of the bytes from bytearray prior to index k.

(extract-bytes bytearray index length)

Returns a new bytearray consisting of length bytes from bytearray, starting at index index. This is functionally equivalent to (take length (drop index bytearray)) with bounds checking added.

(extract-bytes [1 2 3 4 5] 0 1) ⇒ [1]
(extract-bytes [1 2 3 4 5] 0 3) ⇒ [1 2 3]
(extract-bytes [1 2 3 4 5] 2 1) ⇒ [3]
(extract-bytes [1 2 3 4 5] 2 3) ⇒ [3 4 5]

Strings

(string-split string separator)

Splits string at each occurance of separator, returning a list of substrings. The occurances of separator are discarded.

(string-split "hello, world" ", ") ⇒ ("hello" "world")

(string-join list [separator])

Joins the elements of list (which must be strings), separating them with separator.

(string-join '("hello" "world") ", ") ⇒ "hello, world"

(string-trim string [chars])

Trims all characters in chars (which is a string) from each end of string and returns the resulting string. If chars is omitted it defaults to whitespace characters.

(string-trim "+++hello-^-" "+-^") ⇒ "hello"
(string-trim "        hello    ") ⇒ "hello"

(string-trim-left string [chars])

Trims all characters in chars (which is a string) from beginning of string and returns the resulting string. If chars is omitted it defaults to whitespace characters.

(string-trim-left "+++hello-^-" "+-^") ⇒ "hello-^-"
(string-trim-left "        hello    ") ⇒ "hello    "

(string-trim-right string [chars])

Trims all characters in chars (which is a string) from each end of string and returns the resulting string. If chars is omitted it defaults to whitespace characters.

(string-trim-right "+++hello-^-" "+-^") ⇒ "+++hello"
(string-trim-right "        hello    ") ⇒ "        hello"

(string-downcase string)

(string-downcase! string)

string-downcase returns a newly allocated copy of string in which all uppercase letters are changed to lowercase. string-downcase! changes the original string.

(define s "ABCDEFG")        ⇒ "ABCDEFG"
(string-downcase s)         ⇒ "abcdefg"
s                           ⇒ "ABCDEFG"
(string-downcase! s)        ⇒ "abcdefg"
s                           ⇒ "abcdefg"

(string-upcase string)

(string-upcase! string)

string-upcase returns a newly allocated copy of string in which all lowercase letters are changed to uppercase. string-upcase! changes the original string.

(define s "abcdefg")        ⇒ "abcdefg"
(string-upcase s)           ⇒ "ABCDEFG"
s                           ⇒ "abcdefg"
(string-upcase! s)          ⇒ "ABCDEFG"
s                           ⇒ "ABCDEFG"

(string-capitalize string)

(string-capitalize! string)

string-upcase returns a newly allocated copy of string in which all lowercase letters are changed to uppercase. string-capitalize! changes the original string.

(define s "abcdefg")        ⇒ "abcdefg"
(string-capitalize s)       ⇒ "Abcdefg"
s                           ⇒ "abcdefg"
(string-capitalize! s)      ⇒ "Abcdefg"
s                           ⇒ "Abcdefg"

(substring string start end)

Returns a newly allocated string formed from the characters of strin_g beginning with index _start (inclusive) and ending with end (exclusive).

(substring "" 0 0) ⇒ ""
(substring "arduous" 2 5) ⇒ "duo"

(substring? pattern string)

Pattern must be a string. Searches string to see if it contains the substring pattern. Returns #t if pattern is a substring of string, otherwise returns #f.

(substring? "rat" "pirate")  ⇒ #t
(substring? "rat" "outrage") ⇒ #f
(substring? "" any-string)   ⇒ #t

(string-prefix? string1 string2)

Return #t if the first string1 (substring) forms the prefix of the string2; otherwise returns #f.

(string-prefix? "abc" "abcdef") ⇒ #t
(string-prefix? "" any-string)  ⇒ #t
(string-prefix? "abc" "defghi") ⇒ #f

(string-suffix? string1 string2)

Return #t if the first string1 (substring) forms the suffix of the string2; otherwise returns #f.

(string-suffix? "def" "abcdef") ⇒ #t
(string-suffix? "" any-string)  ⇒ #t
(string-suffix? "abc" "defghi") ⇒ #f

(string-length string)

Returns the length of string as an non-negative integer (accounts correctly for multi-byte characters).

(string-length "") ⇒ 0
(string-length "The length") ⇒ 10

(string-null? string)

Returns #t if string has zero length; otherwise returns #f.

(string-null? "") ⇒ #t
(string-null? "Hi") ⇒ #f

(string=? string1 string2)

(string-ci=? string1 string2)

Returns #t if the two strings are the same length and contain the same characters in the same (relative) positions; otherwise returns #f. string-ci=? and doesn’t distinguish uppercase and lowercase letters, but string=? does.

(string=? "PIE" "PIE")
(string=? "PIE" "pie")
(string-ci=? "PIE" "pie")

(string<? string1 string2)

(string-ci<? string1 string2)

(string>? string1 string2)

(string-ci>? string1 string2)

(string=<? string1 string2)

(string-ci=<? string1 string2)

(string=>? string1 string2)

(string-ci=>? string1 string2)

These procedures compare strings according to the order of the characters they contain. The arguments are compared using a lexicographic (or dictionary) order. If two strings differ in length but are the same up to the length of the shorter string, the shorter string is considered to be less than the longer string.

Utility

(random-byte)

Returns a psuedo-random unsigned integer between 0 and 255, inclusive..

(random-byte) ⇒ 13
(random-byte) ⇒ 207

(sleep millis)

Sleep for millis milliseconds.

(sleep 1000)  ;; resumes execution 1 second later

(time sexpr…)

Evaluates each sexpr and reports the time taken to do so.

(write-line sexpr)

Writes the concatenation of the string forms of _sexpr_s followed by a newline.

> (write-line "Hello, " 42 " world")
Hello, 42 world
==> ()

(str sexpr…)

If you provide multiple arguments to str it creates a string from concatenating the string forms of all the _sexpr_s.

(str 1 "." 2) ⇒ "1.2"

(intern string)

Creates a symbol from string. All symbols are interned, and thus have a sole instance for a given sequence of characters. This minimizes overhead and at the same time makes symbol comparison far more efficient.

(gensym)

(gensym prefix)

Created a new, unique symbol made from the supplied prefix (or GENSYM if a prefix is not supplied) and an increasing integer. This is useful when you are generating code and need a unique name (in a macro, for example).

(gensym) ⇒ GENSYM-1
(gensym) ⇒ GENSYM-2
(gensym) ⇒ GENSYM-3

(gensym "hi") ⇒ hi-1
(gensym "ho") ⇒ ho-1
(gensym "hi") ⇒ hi-2
(gensym "ho") ⇒ ho-2
(gensym "ho") ⇒ ho-3
(gensym "hi") ⇒ hi-3

(gensym) ⇒ GENSYM-4

(gensym-naked)

(gensym-naked prefix)

Works the same as gensym, only the unique symbol returned will be a naked symbol.

(gensym-naked) ⇒ GENSYM-1:
(gensym-naked) ⇒ GENSYM-2:
(gensym-naked) ⇒ GENSYM-3:

(gensym-naked "hi") ⇒ hi-1:
(gensym-naked "ho") ⇒ ho-1:
(gensym-naked "hi") ⇒ hi-2:
(gensym-naked "ho") ⇒ ho-2:
(gensym-naked "ho") ⇒ ho-3:
(gensym-naked "hi") ⇒ hi-3:

(gensym-naked) ⇒ GENSYM-4:

(copy sexpr)

Make a copy of the result of evaluating sexpr, IFF it’s mutable. This is limited to lists and association lists. All other values are immutable. Copying an immutable item will return the item, whereas copying a list or association list will make a deep copy of the structure, and return it.

(exec command arg…)

Makes an operating system call. command is the command to execute and the args are the arguments passed on the command line to command. command must be a string, and the args can be anything.

Concurrency

GoLisp has limited concurrency support that is built on top of goroutines and channels.

These functions make use of a process object. This is an opaque piece of data that wraps a custom structure used by the concurrency code; it is returned from fork and schedule and is used by proc-sleep, wake, and abandon to interact with the underlying goroutine.

(fork function)

Executes function in a separate goroutine. When function ends, the goroutine terminates. function takes a single argument which is the process object that is returned.

(define (run-once proc)
  (write-line "start")
  (sleep 1000)
  (write-line "stop"))

(fork run-once)

> start
[a second goes by]
stop
[run-once completes and the goroutine terminates]

(proc-sleep process millis)

Use proc-sleep in a forked function to sleep for millis milliseconds. Using proc-sleep rather than sleep (which can be used) allows code in another process (that has a reference to the process object of the forked code) to preemptively terminate the sleep using the wake function.

proc-sleep returns a boolean that indicates whether the sleep was terminated using wake.

(wake process)

Preemptively terminate a proc-sleep in the code associated with process.

> (define (run proc)
    (do ((woken #f woken))
        (woken (write-line "woken"))
      (write-line "tick")
      (set! woken (proc-sleep proc 10000))))

> (define p (fork run))

tick
tick
[times goes by, tick is printed every 10 seconds]
> (wake p)
woken
[run completes and the goroutine terminates]

(join process)

Blocks the calling function until the process completes or aborts with an error. The return value of the process is returned, or nil if the process ran into an error and aborted. Attempting to call join on a process twice raises an error.

(define (run proc) '(1 2 3))
(define p (fork run))
(join p) ⇒ (1 2 3)

(schedule millis function)

Schedule function to be evaluated in a separate goroutine millis milliseconds from now. function takes a single argument which is the process object that is returned. The process object associated with that goroutine is returned immediately.

> (define (run-delayed proc)
    (write-line "running"))

> (schedule 10000 run-delayed)
[10 seconds pass]
running
[run-delayed completes and the goroutine terminates]

(abandon process)

Cancels the scheduled evaluation associated with process.

> (define (run-delayed proc)
    (write-line "running"))

> (define p (schedule 10000 run-delayed))
[5 seconds pass]
> (abandon p)
[the delay is cancelled and the goroutine terminates]

(reset-timeout process)

Resets the timer on a scheduled process. Causing it to start over. You can use this function to postpone the evaluation of scheduled code.

> (define (run-delayed proc)
    (write-line "running"))

> (define p (schedule 10000 run-delayed))
[less than 10 seconds pass]
> (reset-timeout p)
[10 seconds pass]
running
[run-delayed completes and the goroutine terminates]

Atomic Operations

GoLisp has support for several kinds of atomic operations. These can be useful for protecting memory when working with GoLisp code with concurrent processes, or just Go code with multiple goroutines.

These functions make use of a atomic object. This is an opaque piece of data that wraps an integer; it is returned from atomic and is used by all the atomic-* primitives to interact with the underlying integer using only atomic operations.

(atomic [value])

Creates a new atomic object and returns it. It can optionally be passed a starting value to initialize to. Otherwise, the starting value is 0.

> (atomic)   ⇒ <atomic object with value 0>
> (atomic 5) ⇒ <atomic object with value 5>

(atomic-load atomic)

Loads the current integer value of the atomic object and returns it as an integer.

> (define a (atomic 5))
> (atomic-load a) ⇒ 5

(atomic-store! atomic new)

Stores a new integer value in a atomic object.

> (define a (atomic 5))
> (atomic-store! a 8)
> (atomic-load a) ⇒ 8

(atomic-add! atomic delta)

Adds the delta value to the one stored in the atomic object. The new sum is also returned.

> (define a (atomic 5))
> (atomic-add! a 4) ⇒ 9
> (atomic-load a)   ⇒ 9

(atomic-swap! atomic new)

Swaps the value currently in the atomic object with a new value. The old value is returned.

> (define a (atomic 5))
> (atomic-swap! a 4) ⇒ 5
> (atomic-load a)    ⇒ 4

(atomic-compare-and-swap! atomic old new)

The value in the atomic object is compared to old. If the value matches, the value in the atomic object is swapped with the value in new and true is returned. Otherwise, the values are not swapped and false is returned.

> (define a (atomic 5))
> (atomic-compare-and-swap! a 5 4) ⇒ #t
> (atomic-load a)                  ⇒ 4

> (define b (atomic 5))
> (atomic-compare-and-swap! b 9 4) ⇒ #f
> (atomic-load b)                  ⇒ 5

Channels

Channels are the main way you communicate between goroutines in Go. GoLisp has full support of channels.

(make-channel [buffer-size])

Creates a new channel object with an optional buffer size. If buffer-size is omitted or 0, the channel is unbuffered.

(channel-write channel value)

(channel<- value)

Writes a value to a channel. If the channel is unbuffered or has a full buffer, this call locks until there either another process tries to read from the channel or room is made in the buffer.

(channel-read channel)

(<-channel)

Reads a value from a channel. If the channel is unbuffered or has no buffered data, this call locks until there is data in the channel. <-channel returns two values. The first value is the data read from the channel. The second value is a boolean flag stating whether there is more data in the channel. If the channel is closed and there are no more items left in the buffer, a false flag is returned. Otherwise, a true flag is returned. If a flag of false is returned, the first value will also be nil.

> (define c (make-channel 1))
> (channel-write c 1)
> (c<- 1) ; alternate syntax for the previous line
> (channel-read c) ⇒ (1 #t)
> (<-c)            ⇒ (1 #t) ; alternate syntax for the previous line
> (channel-read c) ; blocks until another process writes to c

(channel-try-write channel value)

Tries to write a value to a channel. If the channel is unbuffered with nobody waiting for a write or has a full buffer, it returns immediately a false value. Otherwise, it writes the value to the channel and returns a true value.

(define c (make-channel 1) (channel-try-write c 1) ⇒ #t

(define c (make-channel)) (channel-try-write c 1) ⇒ #f

(channel-try-read channel)

Tries to reads a value from a channel. This call returns three values. The first is whether data could be read or not. The second is the data that is read, or nil if none was. The last value is whether the channel has more data in it.

> (define c (make-channel 1))
> (c<- 1)
> (channel-try-read c) ⇒ (#t 1 #t)
> (channel-try-read c) ⇒ (#f () #t)

(close-channel channel)

Closes the specified channel. The channel’s buffered is cleared by any other goroutines trying to read from it then all other reads immediately return with the more flag set to false. Trying to write to a closed channel or trying to close a channel twice results in an error.

> (define c (make-channel 1))
> (c<- 1)
> (close-channel c)
> (<-c) ⇒ (1 #t)
> (<-c) ⇒ (() #f)
> (<-c) ⇒ (() #f) ; repeats on subsequent calls
> (channel-try-read c) ⇒ (#t () #f)

Environments

Scheme (and thus GoLisp) is lexically scoped. This is implemented by the creation of a lexical environment (aka symbol table) for each lexical scope:

  • function/lambda/macro invocations, which holds parameters and any local definitions
  • let structures, which hold the let bindings
  • do structures, which hold the do bindings

Functions and lambdas capture a reference to the environment in which they were defined, so they always have access to it’s bindings (that’s a closure, btw).

Each environment has a connection to it’s containing environment, and can override/hide bindings in outer scopes. When a symbol is evaluated, the most local environment is searched first. If a binding for the system isn’t found there, the containing environment is searched. This continues until a binding for the sybol is found or we go all the way to the global environment and still can’t find a binding.

Section 3.2 of [1] does a great job of explaining environments in Scheme, which is the basis for environments in GoLisp.

In Scheme, some environments are more important than others, mainly as they tend to be larger, long lived, and serve as the root of many other environments as a program runs. These are known as top level environments. Specifially, these are the global environment (the only environment that is contained by nothing), and any environments directly below it in the environment tree. The REPL runs in one such environment, which effectively sandboxes it, protecting the bindings in the global environment from corruption.

(environment? sexpr)

Returns #t if sexpr is an environment; otherwise returns #f.

(environment-has-parent? environment)

Returns #t if environment has a parent environment; otherwise returns #f.

(environment-parent environment)

Returns the parent environment of environment. It is an error if environment has no parent.

(environment-bound-names environment)

Returns a newly allocated list of the names (symbols) that are bound by environment. This does not include the names that are bound by the parent environment of environment. It does include names that are unassigned or keywords in environment.

(environment-macro-names environment)

Returns a newly allocated list of the names (symbols) that are bound to syntactic keywords in environment.

(environment-bindings environment)

Returns a newly allocated list of the bindings of environment; does not include the bindings of the parent environment. Each element of this list takes one of two forms: (symbol) indicates that symbol is bound but unassigned, while (symbol object) indicates that symbol is bound, and its value is object.

(environment-reference-type environment symbol)

Returns a symbol describing the reference type of symbol in environment or one of its ancestor environments. The result is one of the following:

  • normal means symbol is a variable binding with a normal value.
  • unassigned means symbol is a variable binding with no value.
  • macro means symbol is a keyword binding.
  • unbound means symbol has no associated binding.

(environment-bound? environment symbol)

Returns #t if symbol is bound in environment or one of its ancestor environments; otherwise returns #f. This is equivalent to

(not (eq? ’unbound
          (environment-reference-type environment symbol)))

(environment-assigned? environment symbol)

Returns #t if symbol is bound in environment or one of its ancestor environments, and has a normal value. Returns #f if it is bound but unassigned. Signals an error if it is unbound or is bound to a keyword.

(environment-lookup environment symbol)

symbol must be bound to a normal value in environment or one of its ancestor environments. Returns the value to which it is bound. Signals an error if unbound, unassigned, or a keyword.

(environment-lookup-macro environment symbol)

If symbol is a keyword binding in environment or one of its ancestor environments, returns the value of the binding. Otherwise, returns #f. Does not signal any errors other than argument-type errors.

(environment-assignable? environment symbol)

symbol must be bound in environment or one of its ancestor environments. Returns #t if the binding may be modified by side effect.

(environment-assign! environment symbol value)

symbol must be bound in environment or one of its ancestor environments, and must be assignable. Modifies the binding to have value as its value, and returns an unspecified result.

(environment-definable? environment symbol)

Returns #t if symbol is definable in environment, and #f otherwise.

(environment-define environment symbol value)

Defines symbol to be bound to object in environment, and returns an unspecified value. Signals an error if symbol isn’t definable in environment.

(eval sexpr environment)

Evaluates sexpr in environment. You rarely need eval in ordinary programs; it is useful mostly for evaluating expressions that have been created “on the fly” by a program.

(system-global-environment)

The function system-global-environment is returns the distinguished environment that’s the highest level ancestor of all other environments. It is the parent environment of all other top-level environments. Primitives, system procedures, and most syntactic keywords are bound in this environment.

(the-environment)

Returns the current environment. This form may only be evaluated in a top-level environment. An error is signalled if it appears elsewhere.

(procedure-environment procedure)

Returns the closing environment of procedure. Signals an error if procedure is a primitive procedure.

(make-top-level-environment [names [values]])

Returns a newly allocated top-level environment. extend-top-level-environment creates an environment that has parent environment, make-top-level-environment creates an environment that has parent system-global-environment, and make- root-top-level-environment creates an environment that has no parent.

The optional arguments names and values are used to specify initial bindings in the new environment. If specified, names must be a list of symbols, and values must be a list of objects. If only names is specified, each name in names will be bound in the environment, but unassigned. If names and values are both specified, they must be the same length, and each name in names will be bound to the corresponding value in values. If neither names nor values is specified, the environment will have no initial bindings.

Environments in GoLisp differ slightly from standard Scheme in that they have a name attached. For the various forms of let and do this is simply "let" and "do", respectively. Not of much use, but then these are just a byproduct of having lexical scopes. What’s more useful is the higher level environments. This brings us to the real reason for adding environment support: game integration sandboxes. When we were writing the game integration functionallity for Engine3, we wanted each game’s event handling to live in a separate sandbox. This is implemented buy creating a new top level environment under the global environment. The problem here is that it’s off in it’s own world, separate from the repl. By naming environments (in this case by the name of the game), we can add a function to return an environment given it’s name. That allows us to peek inside the sandbox from the repl, examining and manipulating the bindings there. And so we added a function to let us do that:

(find-top-level-environment name)

Returns the top level environment with the given name.

Input/Output

(open-input-file filename)

Takes a filename referring to an existing file and returns an input port capable of delivering characters from the file.

(open-output-file filename [append?])

Takes a filename referring to an output file to be created and returns an output port capable of writing characters to a new file by that name.

If append? is given and not #f, the file is opened in append mode. In this mode, the contents of the file are not overwritten; instead any characters written to the file are appended to the end of the existing contents. If the file does not exist, append mode creates the file and writes to it in the normal way.

(close-port port)

Closes port and returns an unspecified value. The associated file is also closed.

(write-bytes byte-array output-port)

Writes byte-array to output-port as a stream of raw bytes. Most usefull for interacting with external devices via serial/usb ports.

(write-string string [output-port])

Writes string to output-port, performs discretionary output flushing, and returns an unspecified value.

(newline [output-port])

Writes an end-of-line to output-port, performs discretionary output flushing, and returns an unspecified value.

(write sexpr [output-port])

Writes a written representation of sexpr to output-port, and returns an unspecified value. If sexpr has a standard external representation, then the written representation generated by write shall be parsable by read into an equivalent object. Thus strings that appear in the written representation are enclosed in doublequotes, and within those strings backslash and doublequote are escaped by backslashes. write performs discretionary output flushing and returns an unspecified value.

(read [input-port])

Converts external representations of Scheme objects into the objects themselves. read returns the next object parsable from input-port, updating input-port to point to the first character past the end of the written representation of the object. If an end of file is encountered in the input before any characters are found that can begin an object, read returns an end-of-file object. The input-port remains open, and further attempts to read will also return an end-of-file object. If an end of file is encountered after the beginning of an object’s written representation, but the written representation is incomplete and therefore not parsable, an error is signalled.

(eof-object? sexpr)

Returns #t if objec_sexpr_ is an end-of-file object; otherwise returns #f.

(format destination control-string argument…)

Writes the characters of control-string to destination, except that a tilde (~) introduces a format directive. The character after the tilde, possibly preceded by prefix parameters and modifiers, specifies what kind of formatting is desired. Some directives use an argument to create their output; the typical directive puts the next argument into the output, formatted in some special way. It is an error if no argument remains for a directive requiring an argument.

The output is sent to destination. If destination is #f, a string is created that contains the output; this string is returned as the value of the call to format. If destination is #t, the output is sent to stdout. In all other cases format returns an unspecified value. Otherwise, destination must be an output port, and the output is sent there.

A format directive consists of a tilde (~), an optional prefix parameter, an optional at-sign (@) modifier, and a single character indicating what kind of directive this is. The alphabetic case of the directive character is ignored. The prefix parameters are generally integers, notated as optionally signed decimal numbers.

In place of a prefix parameter to a directive, you can put the letter V (or v), which takes an argument for use as a parameter to the directive. Normally this should be an integer. This feature allows variable-width fields and the like. You can also use the character # in place of a parameter; it represents the number of arguments remaining to be processed.

~A: The next argument, which may be any object, is printed as if by write-line. ~mincolA inserts spaces on the right, if necessary, to make the width at least mincol columns. The @ modifier causes the spaces to be inserted on the left rather than the right.

~S: The next argument, which may be any object, is printed as if by write (in as readable format as possible). ~mincolS inserts spaces on the right, if necessary, to make the width at least mincol columns. The @ modifier causes the spaces to be inserted on the left rather than the right.

~%: This outputs a newline character. This outputs a #\newline character. ~n% outputs n newlines. No argument is used. Simply putting a newline in control-string would work, but ~% is often used because it makes the control string look nicer in the middle of a program.

~~: This outputs a tilde. ~n~ outputs n tildes.

~newline: Tilde immediately followed by a newline ignores the newline and any following whitespace characters. With an @, the newline is left in place, but any following whitespace is ignored. This directive is typically used when control-string is too long to fit nicely into one line of the program:

(define (type-clash-error procedure arg spec actual)
   (format
    #t
    "~%Procedure ~S~%requires its %A argument ~
     to be of type ~S,~%but it was called with ~
     an argument of type ~S.~%"
    procedure arg spec actual))
(type-clash-error ’vector-ref
                  "first"
                  'integer
                  'vector)

prints

Procedure vector-ref
requires its first argument to be of type integer,
but it was called with an argument of type vector.

Note that in this example newlines appear in the output only as specified by the ~% directives; the actual newline characters in the control string are suppressed because each is preceded by a tilde.

Frames

GoLisp contains a frame system inspired by Self [4] and NewtonScript [5].

A frame is a set of named slots that hold arbitrary values. Slot names must be symbols that end with a colon. For example: color:, or height:. When evaluated normally, these special symbols don’t get looked up in the environment, they simply evaluate to themselves.

(define a a:)

'a ⇒ a
a  ⇒ a:

'a: ⇒ a:
a:  ⇒ a:

Basic functions

(make-frame slot-name slot-value … )

Frames can be created using the make-frame function, passing it an alternating sequence of slot names and values:

(make-frame a: 1 b: 2)

This results in a frame with two slots, named a: and b: with values 1 and 2, respectively.

{ slot-name slot-value … }

This is an alternative syntax for defining frames:

{a: 1 b: 2}

Both are equivalent. Slot names and values in both cases are evaluated (this is one reason for the non-evaluating symbols: it avoiding having to quote literal slot names).

(clone frame)

Frames represent things. For example, you could use a frame that looks like {x: 1 y: 10} to represent a point. A system that would use point frames will typically need many independant points. The approach to this is to create a prototypical point data frame, and use the clone function to create individual, independant frames:

(define point {x: 1 y: 1})
(define p1 (clone point))
(set-slot! p1 x: 5)
(get-slot p1 x:)    ⇒ 5
(get-slot point x:) ⇒ 1

(has-slot? frame slot-name)

(slot-name? frame)

The has-slot? function is used to query whether a frame contains (directly or in an ancestor) the particular slot:

(define f {a: 1 b: 2})
(has-slot? f a:) ⇒ #t
(a:? f) ⇒ #t
(has-slot? f c:) ⇒ #f
(c:? f) ⇒ #f

(get-slot frame slot-name)

(slot-name frame)

The get-slot function is used to retrieve values from frame slots:

(define f {a: 1 b: 2})
(get-slot f a:) ⇒ 1
(a: f) ⇒ 1
(get-slot f b:) ⇒ 2
(b: f) ⇒ 2

If the frame passed to get-slot contains a slot with the specified name, it’s value is returned. If not, then parent frames are searched in a nondeterministic order until a slot with the specified name is found. If a matching slot is found, it’s value is returned. If none is found an error is raised.

(define f {a: 1 b: 2})
(define g {parent*: f c: 3})

(get-slot g c:) ⇒ 3
(get-slot g a:) ⇒ 1  ; from the frame f

(get-slot-or-nil frame slot-name)

The same as above, except that if a matching slot is not found, nil is returned instead of raising an error.

(set-slot! frame slot-name new-value)

(slot-name! frame new-value)

The set-slot! function is used to change values in frame slots:

(define f {a: 1 b: 2})
(get-slot f a:)    ⇒ 1
(set-slot! f a: 5) ⇒ 5
(a:! f 5) ⇒ 5
(get-slot f a:)    ⇒ 5

Trying to set a slot that doesn’t exist in the frame will result in a corresponding slot being created.

(define f {a: 1 b: 2})
(set-slot! f c: 5) ⇒ 5
f                  ⇒ {a: 1 b: 2 c: 5}

(remove-slot! frame slot-name)

The remove-slot! function is used to function is used to remove a slot from a frame. It only removes slots from the frame itself. not any of it’s parents. remove-slot! return #t if the slot was removed, #f otherwise.

(define f {a: 1 b: 2})
(remove-slot! f a:) ⇒ #t
f                   ⇒ {b: 2}
(remove-slot! f a:) ⇒ #f

(frame-keys frame)

Returns a list of the slot names in frame. Note that the order of the result is nondeterministic.

(frame-keys {a: 1 b: 2}) ⇒ (a: b:)

(frame-values frame)

Returns a list of the slot values in frame. Note that the order of the result is nondeterministic.

(frame-values {a: 1 b: 2}) ⇒ (1 2)

Parent slots

Frames can have slots that refer to other slots to provide prototype inheritance. These slots have names that have a * immediately preceeding the trailing :, for example proto*:. The names of the parent slots don’t matter; it is the trailing *: in the name that marks them as parent slots. A frame can have any number of parent slots.

When a slot is being searched for, if it isn’t found in the specified slot, these parent slots are recursively searched until the requested slot is found or the entire graph has been examined.

> (define y {a: 1})
==> {a: 1}
> (define x {b: 2 p*: y})
==> {b: 2 p*: {...}}

> (a: x)
==> 1

Note: Parent slots are searched in arbitrary order.

Function slots

Now things get interesting. Slot values can be functions (typically lambda expressions) as well as data. Function slots can be executed by using the send function

(send frame slot-name arg…)

(slot-name> frame arg…)

(define f {add: (lambda () (+ 1 2))})
(send f add:) ⇒ 3
(add:> f) ⇒ 3

As expected, parameters are supported:

(define f {add: (lambda (x) (+ 1 x))})
(send f add: 2) ⇒ 3

In the body of a function, slots can be refrenced like normal variables. To do so, simply omit the trailing colon:

(define f {a: 3
           add: (lambda (x) (+ a x))})
(send f add: 2) ⇒ 5

Likewise, functions in the frame (or parent frames) can be referred to directly by name.

(define f {a: 5
           b: 2
           foo: (lambda (x) (+ x a))
           bar: (lambda () (foo b))})
(send f bar:) ⇒ 7

Bindings defined in the local environment (e.g. by a let form) hide frame slots of the same name. In the following, let overrides the a: slot by introducing a local binding for a.

(let ((f {a: 42})
      (g {parent*: f  foo: (lambda ()
                             (let ((a 10))
                               (+ 1 a)))}))
(send g foo:) ⇒ 11

Of course the end game of all this is to be able to inherit functions from parent frames:

(define f {a: 5
           foo: (lambda (x) (+ x a))})
(define g {parent*: f
           b: 2
           bar: (lambda () (foo b))})
(send g bar:) ⇒ 7

Notice that we’ve been saying parent frames, i.e. plural. Also note that parent slot names are arbitrary and for documentation purposes only. A frame can have any number of parents. When a slot is looked for, the explictily specified frame is searched first, recursively followed by parent frames in a nondeterministic order until a matching slot is found. If none are found, the result is nil.

(define e {a: 5})
(define f {b: 2})
(define g {parent-e*: e
           parent-f*: f
           foo: (lambda (x) (+ x a))
           bar: (lambda () (foo b))}))
(send g bar:)       ⇒ 7
(set-slot! g a: 10)
(get-slot g a:)     ⇒ 10

When you set a slot with parent frames involved, if the slot is found in the explicit frame it’s value is set and the new value is returned. If it doesn’t exist in the explicit frame or any parent frame, it gets created in the explicit frame and the value returned. Otherwise the parents are searched until the slot is found and it’s value is set and returned.

(send-super slot-name arg…)

(slot-name^ arg…)

Like send, but sends to the first parent that has the named slot. send-super can only be used from within a function slot.

(apply-slot frame slot-name sexpr…)

Apply the function that results from evaluating the function in slot slot-name of frame to the argument list resulting from evaluating each sexpr.

Each initial sexpr can evaluate to any type of object, but the final one (and there must be at least one sexpr) must evaluate to a list.

(define f {foo: (lambda (x y z) (+ 1 x y z))})
(apply-slot f foo: 2 '(3 4)) ⇒ 10
(apply-slot f foo: '(2 3 4)) ⇒ 10

(apply-slot-super slot-name sexpr…)

Like apply-slot, but sends to the first parent that has the named slot. apply-slot-super can only be used from within a function slot.

Dynamic inheritence

Parent slots are slots like any other and can have their values changed at any time. This ability is somewhat unusual fort those with a heavy OO background but can be very useful for changing behavior on the fly. A prime example of this is the implimentation of a state machine. The functions for each state can be placed in different frames and transitions can modify the slot contaiing that state behavior.

Here’s an example of this.

(define state {name: ""
               enter: (lambda ())
               halt: (lambda ())
               set-speed: (lambda (s))
               halt: (lambda ())
               transition-to: (lambda (s)
                                (set! state* s)
                                (enter))})

(define stop-state {name: "stop"
                    parent*: state
                    enter: (lambda ()
                             (set! speed 0)
                             (transition-to idle-state))})

(define idle-state {name: "idle"
                    parent*: state
                    set-speed: (lambda (s)
                                 (set! speed s)
                                 (transition-to start-state))})

(define start-state {name: "start"
                     parent*: state
                     halt: (lambda ()
                             (transition-tostop-state))
                     set-speed: (lambda (s)
                                  (set! speed s)
                                  (transition-to change-speed-state))})

(define change-speed-state {name: "change-speed"
                            parent*: state
                            halt: (lambda ()
                                    (transition-to stop-state))
                            set-speed: (lambda (s)
                                         (set! speed s))})

(define motor {speed: 0
               state*: state
               start: (lambda () (transition-to stop-state)) })

Now you can do things like the following:

(send motor start:)
motor ⇒ {speed: 0 state*: {name: "idle" ...}}
(send motor set-speed: 10)
motor ⇒ {speed: 10 state*: {name: "start" ...}}
(send motor set-speed: 20)
motor ⇒ {speed: 20 state*: {name: "change-speed" ...}}
(send motor set-speed: 15)
motor ⇒ {speed: 15 state*: {name: "change-speed" ...}}
(send motor halt:)
motor ⇒ {speed: 0 state*: {name: "idle" ...}}

Json support

GoLisp has built-in support for converting between stringified Json and frames.

(json->lisp string)

(json->lisp "{'key': [1, 2, 3]}") ⇒ {key: (1 2 3)}

(lisp->json frame)

(lisp->json {key: (1 2 3)}) ⇒ "{'key': [1, 2, 3]}"

Testing

Golisp has a builtin testing framework, completely written in GoLisp.

(context tag setup sexpr…)

The macro context takes a name (usually a string, but it can be a plain symbol) as it’s first argument. This shoudl describe what the context focusses on. Next is a list of sexprs that are executed before each of the following sexprs. These sexprs should be ‘it’ expressions, each of which focus on a specific aspect of the context.

(it tag assertion…)

This defines a cohesive block of assertions. The context’s setup code will be run in a new environment for each it block.

(assert-true )

Passes if sexpr evaluates to a truthy value, fails otherwise.

(assert-false )

Passes if sexpr evaluates to a falsy value, fails otherwise.

(assert-eq )

Passes if the result of evaluating actual is equal to the result of evaluating expected, fails otherwise.

(assert-neq )

Passes if the result of evaluating actual is not equal to the result of evaluating expected, fails otherwise.

(assert-nil )

Passes is sexpr evaluates to nil, fails otherwise.

(assert-not-nil )

Passes is sexpr evaluates to anything other than nil, fails otherwise.

(assert-error )

Passes if evaluating sexpr results in an error being signalled, passes if it evaluates without problems.

Generally you should create a test file for each feature you are testing. The file is a plain lisp file and can contain any lisp code, including global variable and function definitions.

For example, here is the test file for scoping:

(context "environments"

  ((define a 5)
   (define (foo a)
     (lambda (x)
      (+ a x))))

  (it "can access a in the global env"
      (assert-eq a 5))

  (it "gets a from the function's local env"
      (assert-eq ((foo 1) 5) 6)
      (assert-eq ((foo 2) 5) 7)
      (assert-eq ((foo 10) 7) 17)))

Running a test results in a stream of status output for each test, followed at the very end by a summary. Running the above results in the following:

environments

  can access a in the global env
    (assert-eq a 5)

  gets a from the function's local env
    (assert-eq ((foo 1) 5) 6)
    (assert-eq ((foo 2) 5) 7)
    (assert-eq ((foo 10) 7) 17)

Ran 4 tests in 0.003 seconds
4 passes, 0 failures, 0 errors

If we introduce a failure, the output would be:

environments

  can access a in the global env
    (assert-eq a 5)

  gets a from the function's local env
    (assert-eq ((foo 1) 5) 6)
    (assert-eq ((foo 2) 5) 8)
      - expected 8, but was 7
    (assert-eq ((foo 10) 7) 17)

Ran 4 tests in 0.002 seconds
3 passes, 1 failures, 0 errors

Failures:
  environments gets a from the function's local env:
    (assert-eq ((foo 2) 5) 8)
      - expected 8, but was 7

Errors are also reported. Errors are problems that occur while evaluating the clauses, that aren’t failures. Essentially they indicate bugs of some sort.

environments

  can access a in the global env
    (assert-eq a 5)

  gets a from the function's local env
    (assert-eq ((foo 1) 5) 6)
    ERROR: Quotent: (7 0) -> Divide by zero.

Ran 3 tests in 0.002 seconds
2 passes, 0 failures, 1 errors

Errors:
  environments gets a from the function's local env:
    ERROR: Quotent: (7 0) -> Divide by zero.

The above output was generated by the testing framwork running in verbose mode. You can also run in quiet mode which only outputs the summary:

Ran 4 tests in 0.003 seconds
4 passes, 0 failures, 0 errors

You run tests by running the golisp repl in test mode, providing either a directory or filename. If you provide a directory all files in it that match *_test.lsp will be run. If you provide a filename, only that file will be run.

$golisp -t tests/scope_test.lsp

Ran 4 tests in 0.002 seconds
4 passes, 0 failures, 0 errors


$golisp -t tests

Ran 935 tests in 0.273 seconds
935 passes, 0 failures, 0 errors

Adding the -v flag will produce the detailed output above.

Defining primitives

The Go function MakePrimitiveFunction allows you to create primitive functions.

MakePrimitiveFunction(name string, argCount string,
                      function func(*Data, *SymbolTableFrame)(*Data, error))

The arguments are:

  1. The function name. This is the name of a symbol which will be used to reference the function.

  2. An argument count expectation. This is a string that specifies how many arguments the primitive expects. It can take several forms:
    • A single, specific number. E.g. exactly two: "2"
    • A minimum number. E.g. at least two: ">=2"
    • A range of values. E.g. between two and five, inclusive: "(2,5)"
    • One of a selection of the above: E.g. "2|3|>=5"
    • An unspecified number, any checking must be done in the primitive definition: "*"
  3. The Go function which implements the primitive. This function must have the signature func <Name>(*Data, *SymbolTableFrame) (*Data, error)

The implementing function takes two parameters as seen above:

  1. A Lisp list containing the arguments

  2. The environment in which the primitive is being evaluated. This is used when calling Eval or Apply.

Primitives use, like functions defined in LISP, applicative evaluation order. That means that all arguments are evaluated and the resulting values passed to the function. This frees you from having to evaluate the arguments and handle errors. You still have to verify the number of arguments (only if you used -1 as trhe argument cound in the MakePrimitiveFunction call) and their type, if aplicable.

An example:

MakePrimitiveFunction("!", "1", BooleanNot)

func BooleanNot(args *Data, env *SymbolTableFrame) (result *Data, err error) {
    val := BooleanValue(Car(args))
    return BooleanWithValue(!val), nil
 }

You can extend the goLisp runtime without changing any of it’s code. You simply import the golisp package (typically aliased to . to make the code less noisy) and place calls to MakePrimitiveFunction in your package’s init block.

There is another, very similar function that you will typically not need unless you are hackign on the language itself (as opposed to adding builting functions):

MakeSpecialForm(name string, argCount int,
                function func(*Data, *SymbolTableFrame) (*Data, error))

Arguments and the signature of the implementing function are identical to MakePrimitiveFunction. The only difference is that this defines a special form which uses normal evaluation order. I.e. arguments are not evaluated before calling the function; the raw sexpressions are passed in. Thus the implementing function has full control over what gets evaluated and when.

MakeSpecialForm("if", "2|3", IfImpl)

func IfImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
    c, err := Eval(Car(args), env)
    if err != nil {
        return
    }

    if BooleanValue(c) {
        return Eval(Cadr(args), env)
    } else {
        return Eval(Caddr(args), env)
    }
}

Data

The core lisp data element is the data type which logically contains a type tag and a value. The type tags are defined by the constants: ConsCellType, NumberType, BooleanType, StringType, SymbolType, FunctionType, PrimitiveType, ObjectType.

The types are described earlier. If you need to check the type of a piece of data you can fetch it’s type using the TypeOf(*Data) int function and then compare it to a type tag constant. Additionally there are predicate functions for the most common types:

StringP(*Data) bool returns whether the data is a string

SymbolP(*Data) bool returns whether the data is a symbol

NumberP(*Data) bool returns whether the data is a number

IntegerP(*Data) bool returns whether the data is an integer

FloatP(*Data) bool returns whether the data is a float

PairP(*Data) bool returns whether the data is a cons cell (aka pair)

ListP(*Data) bool is an alias for PairP

ObjectP(*Data) bool returns whether the data is an encapsulated Go object

FunctionP(*Data) bool returns whether the data is either a user defined or primitive function

Two other very handy functions are:

NilP(*Data) bool returns whether the data is nil

NotNilP(*Data) bool returns whether the data is non-nil

Creating and accessing data

There are various convenience functions that you can use to create data:

Cons(car *Data, cdr *Data) *Data creates a cons cell with the provided values for it’s car and cdr.

IntegerWithValue(n int64) *Data creates a number (integer) with the provided value

FloatWithValue(n float32) *Data creates a number (float) with the provided value

BooleanWithValue(b bool) *Data creates a boolean with the provided value

StringWithValue(s string) *Data creates a string with the provided value

SymbolWithName(s string) *Data creates a symbol with the provided name

ObjectWithTypeAndValue(typeName string, o unsafe.Pointer) *Data creates an encapsulated Go object with the given type tag and value

Similarly there are functions for extracting information from data elements:

IntegerValue(*Data) int64 extracts the primitive integer value

FloatValue(*Data) float32 extracts the primitive float value

StringValue(*Data) string extracts the primitive string value

BooleanValue(*Data) bool extracts the primitive boolean value

TypeOfObject(*Data) string extracts the type tag of the encapsulated Go object

ObjectValue(*Data) unsafe.Pointer extracts the primitive object value

String(*Data) string returns a human readable string representation of the data

Length(*Data) int returns the length of a piece of Lisp data. This will be 0 in all cases except lists, when it will be the number of elements in the list.

IsEqual(*Data, o *Data) bool compares two expressions:

  • if the two pointers point to the same thing, they are equal

  • nil is never equal to anything

  • data of different types are never equal

  • if the data are lists, they are equal if corresponding pairs of elements are equal

  • if values are the same (e.g. strings & numbers) they are equal

Any of the above ...Value functions return the corresponding zero value if the data is not of the appropriate type.

Working with lists

Car(*Data) *Data return the car pointer from a cons cell (nil otherwise)

Cdr(*Data) *Data) return the cdr pointer from a cons cell (nil otherwise)

Caar through Cddddr returns a piece of the list as expected

First(*Data) *Data Return the first item in a list (equivalent to car) … Fifth(*Data) *Data return the fifth item in a list

Nth(*Data, int) *Data return the nth item in a list (starting at 1)

ArrayToList(sexprs []*Data) *Data converts an array of Lisp data objects to a Lisp list.

Evaluation

These functions are the entry points into code evaluation. The main reason to use these, and especially Eval, is to evaluate arguments to primitives as required.

Eval(d *Data, env *SymbolTableFrame) (*Data, error) evaluates the expression in d in the provided environment and returns the result along with any error that occurred during evaluation. If an error is returned, the value of the data result is indeterminate.

Apply(function *Data, args *Data, env *SymbolTableFrame) (*Data, error) takes a function element, either user defined or primitive, and applies it to the provided arguments in the provided environment. This is actually how Eval deals with a list: the evaluation of the car of the list is the function, the cdr is the list of arguments. It’s up to the primitive implementation to evaluate arguments as required.

The implementation of if serves as an example:

func IfImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
  c, err := Eval(Car(args), env)
  if err != nil {
      return
  }

  if BooleanValue(c) {
      return Eval(Cadr(args), env)
  } else {
      return Eval(Caddr(args), env)
  }
}

Note that Cadr and Caddr return nil if the corresponding element is missing from the argument list. Also, passing a nil to Eval returns nil.

Embedded GoLisp

Now that you know what’s supported in GoLisp, and how to manipulate it’s structures and code in from Go, you need to get code into it. There are two ways to do this.

Single expression strings

If you have a Go string that contains a single sexpr, you can pass it to Parse which returns the list containing the parsed string and an error value. The returned structure can be passed to Eval to evaluate it. The resulting sexpr is returned.

For example, here is the REPL:

prompt := "> "
LoadHistoryFromFile(".golisp_history")
lastInput := ""
for true {
    input := *ReadLine(&prompt)
    if input != "" {
        if input != lastInput {
            AddHistory(input)
        }
        lastInput = input
        code, err := Parse(input)
        if err != nil {
            fmt.Printf("Error: %s\n", err)
        } else {
            d, err := Eval(code, Global)
            if err != nil {
                fmt.Printf("Error in evaluation: %s\n", err)
            } else {
                fmt.Printf("⇒ %s\n", String(d))
            }
        }
    }
}

Loading a file

If you have GoLisp code in a plain text file you can pass the filename to ProcessFile. This will parse and evaluate each sexpr contained in the file. The result of ProcessFile is the value of the evaluation of the final sexpr in the file and an error value.

Generally, you load a file for it’s side effects: defining values and functions.

The REPL

You typically import the golisp package into your Go app and use it. However, you can run the golisp/main/golisp package, which puts you into the REPL where you can enter and evaluate one line at a time. You can use the load function to load/evaluate files of golisp code you have written. If you provide a list of filenames on the command line they will be loaded and evaluated before dropping you into the REPL.

Note that you can easily make the REPL available to the user from within your application.

References

[1] Harold Abelson, Gerald Jay Sussman, and Julie Sussman. Structure and Interpretation of Computer Programs. MIT Press, Cambridge, Mass., 1985.

[2] Richard P. Gabriel and Kent M. Pitman. Endpaper: Technical issues of separation in function cells and value cells. Lisp and Symbolic Computation, 1(1):81–101, June 1988.

[3] Guy L. Steele. COMMON LISP: the language. Digital Press, 12 Crosby Drive, Bedford, MA 01730, USA, 1984. With contributions by Scott E. Fahlman and Richard P. Gabriel and David A. Moon and Daniel L. Weinreb.

[4] David Ungar and Randall B. Smith. self: the power of simplicity. In OOPSLA 87 Conference Proceedings, pages 227–241, Orlando, Florida, October 1987.

[5] Apple Computer. The NewtonScript Programming Language. Apple Computer, Cupertino, Ca., 1996.