Skip to content

Syntax Forms

These are special forms handled by the compiler, not procedure dispatch. They cannot be passed as arguments or stored in variables. Available from (scheme base).


Definitions

define

(define var expr) | (define (name params ...) body ...)

Binds var to the result of evaluating expr, or defines a procedure named name with the given params and body. The procedure shorthand (define (f x) ...) is equivalent to (define f (lambda (x) ...)). At the top level, define creates a new binding. Inside a lambda, let, or other body, define creates an internal definition (equivalent to letrec*).

kaappi> (define pi 3.14159)
kaappi> pi
;=> 3.14159
kaappi> (define (square x) (* x x))
kaappi> (square 5)
;=> 25
kaappi> (define (fact n)
         (if (<= n 1) 1 (* n (fact (- n 1)))))
kaappi> (fact 10)
;=> 3628800

See also: lambda, set!, define-syntax


lambda

(lambda (params ...) body ...)
(lambda (params ... . rest) body ...)
(lambda rest body ...)

Creates an anonymous procedure. The params list specifies the formal parameters. A dotted tail parameter collects extra arguments into a list. A bare symbol instead of a parameter list collects all arguments into a list.

kaappi> ((lambda (x y) (+ x y)) 3 4)
;=> 7
kaappi> (define add1 (lambda (x) (+ x 1)))
kaappi> (add1 41)
;=> 42
kaappi> ((lambda (x . rest) rest) 1 2 3 4)
;=> (2 3 4)
kaappi> ((lambda args (length args)) 'a 'b 'c)
;=> 3

See also: define, case-lambda


Conditionals

if

(if test consequent) | (if test consequent alternate)

Evaluates test. If the result is true (anything other than #f), evaluates and returns consequent. Otherwise, evaluates and returns alternate. If test is false and no alternate is provided, the result is unspecified.

kaappi> (if (> 3 2) 'yes 'no)
;=> yes
kaappi> (if (< 3 2) 'yes 'no)
;=> no
kaappi> (if #f 'yes)
kaappi> (if 0 'yes 'no)
;=> yes

See also: cond, when, unless


cond

(cond (test expr ...) ... (else expr ...))

Evaluates each test in order until one returns true, then evaluates the corresponding expr sequence and returns the last result. An else clause matches if no previous test succeeded. A clause of the form (test => proc) passes the test result to proc.

kaappi> (cond ((> 3 2) 'greater)
              ((< 3 2) 'less)
              (else 'equal))
;=> greater
kaappi> (cond ((assv 2 '((1 one) (2 two) (3 three)))
               => cadr)
              (else 'not-found))
;=> two

See also: if, case, guard


case

(case key ((datum ...) expr ...) ... (else expr ...))

Evaluates key, then compares its value (using eqv?) against each list of datums. The expr sequence of the first matching clause is evaluated and its result returned. An else clause matches when no datum matches. A clause of the form ((datum ...) => proc) calls proc with the key value.

kaappi> (case (* 2 3)
         ((2 3 5 7) 'prime)
         ((1 4 6 8 9) 'composite)
         (else 'unknown))
;=> composite
kaappi> (case (car '(c d))
         ((a e i o u) 'vowel)
         ((w y) 'semivowel)
         (else 'consonant))
;=> consonant

See also: cond, if


and

(and test ...)

Evaluates each test from left to right. If any evaluates to #f, returns #f immediately without evaluating the rest. If all tests are true, returns the value of the last test. (and) with no arguments returns #t.

kaappi> (and 1 2 3)
;=> 3
kaappi> (and 1 #f 3)
;=> #f
kaappi> (and)
;=> #t
kaappi> (and (> 3 2) (< 5 10) 'yes)
;=> yes

See also: or, if


or

(or test ...)

Evaluates each test from left to right. If any evaluates to a true value, returns that value immediately. If all tests are #f, returns #f. (or) with no arguments returns #f.

kaappi> (or #f #f 3)
;=> 3
kaappi> (or #f #f #f)
;=> #f
kaappi> (or)
;=> #f
kaappi> (or (memq 'b '(a b c)) 'not-found)
;=> (b c)

See also: and, if


when

(when test expr ...)

If test is true, evaluates each expr in sequence and returns the value of the last one (like an implicit begin). If test is false, the result is unspecified. when is a one-armed if with implicit begin.

kaappi> (when (> 3 2)
         (display "3 is greater")
         (newline)
         'done)
3 is greater
;=> done
kaappi> (when #f (error "never reached"))

See also: unless, if


unless

(unless test expr ...)

The opposite of when. If test is false, evaluates each expr in sequence and returns the value of the last one. If test is true, the result is unspecified.

kaappi> (unless (> 3 5)
         (display "3 is not greater than 5")
         (newline)
         'done)
3 is not greater than 5
;=> done
kaappi> (unless #t (error "never reached"))

See also: when, if


Binding Forms

let

(let ((var expr) ...) body ...)
(let name ((var expr) ...) body ...)

Binds each var to the value of its corresponding expr (evaluated in an unspecified order), then evaluates body in the extended environment. Named let binds name to a procedure that can be called recursively from body, enabling loop constructs.

kaappi> (let ((x 2) (y 3))
         (+ x y))
;=> 5
kaappi> (let loop ((i 0) (sum 0))
         (if (> i 10)
             sum
             (loop (+ i 1) (+ sum i))))
;=> 55

See also: let*, letrec, let-values


let*

(let* ((var expr) ...) body ...)

Like let, but each binding is evaluated sequentially, and each subsequent binding can refer to earlier ones. This is equivalent to nested let forms.

kaappi> (let* ((x 1) (y (+ x 1)) (z (+ x y)))
         (list x y z))
;=> (1 2 3)
kaappi> (let* ((path "/tmp")
               (file (string-append path "/data.txt")))
         file)
;=> "/tmp/data.txt"

See also: let, letrec*


letrec

(letrec ((var expr) ...) body ...)

Binds each var, then evaluates each expr in the extended environment where all variables are visible (but not yet assigned). This allows mutually recursive definitions. It is an error to reference a variable before its initializer has completed, except inside a lambda.

kaappi> (letrec ((even? (lambda (n)
                          (if (= n 0) #t (odd? (- n 1)))))
                 (odd? (lambda (n)
                         (if (= n 0) #f (even? (- n 1))))))
         (list (even? 4) (odd? 3)))
;=> (#t #t)

See also: letrec*, let, define


letrec*

(letrec* ((var expr) ...) body ...)

Like letrec, but each expr is evaluated sequentially from left to right. Each initializer can refer to the values of previously initialized variables. This is equivalent to internal define at the top of a body.

kaappi> (letrec* ((x 1)
                  (y (+ x 1)))
         (+ x y))
;=> 3

See also: letrec, let*


let-values

(let-values (((var ...) expr) ...) body ...)

Like let, but each expr can return multiple values (via values), which are bound to the corresponding vars. The bindings are evaluated in an unspecified order.

kaappi> (let-values (((a b) (values 1 2))
                     ((c d e) (values 3 4 5)))
         (list a b c d e))
;=> (1 2 3 4 5)
kaappi> (let-values (((q r) (floor/ 17 5)))
         (list q r))
;=> (3 2)

See also: let*-values, values


let*-values

(let*-values (((var ...) expr) ...) body ...)

Like let-values, but bindings are evaluated sequentially. Each expr can refer to variables bound by previous clauses.

kaappi> (let*-values (((a b) (values 1 2))
                      ((c) (+ a b)))
         c)
;=> 3

See also: let-values, let*


Iteration

do

(do ((var init step) ...) (test expr ...) command ...)

Iterative construct. Each var is bound to init, then repeatedly: test is evaluated; if true, the expr sequence is evaluated and its last value returned; otherwise the command sequence is evaluated for side effects, each var is updated to its step value, and iteration continues.

kaappi> (do ((i 0 (+ i 1))
             (sum 0 (+ sum i)))
            ((= i 5) sum))
;=> 10
kaappi> (do ((vec (make-vector 5))
             (i 0 (+ i 1)))
            ((= i 5) vec)
         (vector-set! vec i (* i i)))
;=> #(0 1 4 9 16)

See also: let (named let for loops), for-each


case-lambda

(case-lambda (formals body ...) ...)

Creates a procedure that dispatches on the number of arguments. Each clause has its own formals list and body. When the procedure is called, the first clause whose formals match the argument count is selected.

kaappi> (define add
         (case-lambda
           ((x) x)
           ((x y) (+ x y))
           ((x y z) (+ x y z))))
kaappi> (add 1)
;=> 1
kaappi> (add 1 2)
;=> 3
kaappi> (add 1 2 3)
;=> 6
kaappi> (define optarg
         (case-lambda
           ((x) (optarg x 10))
           ((x y) (+ x y))))
kaappi> (optarg 5)
;=> 15
kaappi> (optarg 5 20)
;=> 25

See also: lambda, define


Sequencing and Mutation

quote

(quote datum) | 'datum

Returns datum without evaluating it. The shorthand 'datum is equivalent to (quote datum). The returned value is immutable.

kaappi> (quote (1 2 3))
;=> (1 2 3)
kaappi> '(a b c)
;=> (a b c)
kaappi> 'hello
;=> hello
kaappi> '#(1 2 3)
;=> #(1 2 3)

See also: quasiquote


set!

(set! var expr)

Assigns the value of expr to the existing binding of var. It is an error if var has not been defined. Unlike define, set! does not create a new binding -- it mutates an existing one.

kaappi> (define x 1)
kaappi> x
;=> 1
kaappi> (set! x 42)
kaappi> x
;=> 42
kaappi> (define count 0)
kaappi> (set! count (+ count 1))
kaappi> count
;=> 1

See also: define


begin

(begin expr ...)

Evaluates each expr in sequence from left to right and returns the value of the last expression. At the top level of a program or library body, begin splices its contents into the enclosing body.

kaappi> (begin
         (define x 1)
         (set! x (+ x 1))
         x)
;=> 2
kaappi> (begin (display "hello ") (display "world") (newline) 42)
hello world
;=> 42

See also: when, unless, lambda


quasiquote

`template | (quasiquote template)

Like quote, but allows unquoting inside the template. An unquote expression ,expr evaluates expr and inserts its value. An unquote-splicing expression ,@expr evaluates expr (which must produce a list) and splices its elements into the surrounding list.

kaappi> (let ((x 1) (y 2))
         `(sum is ,(+ x y)))
;=> (sum is 3)
kaappi> (let ((items '(a b c)))
         `(begin ,@items end))
;=> (begin a b c end)
kaappi> `(list ,(+ 1 2) 4)
;=> (list 3 4)

See also: quote


Macros

define-syntax

(define-syntax name transformer-spec)

Binds name as a macro. The transformer-spec is typically a syntax-rules form that specifies how to rewrite calls to name.

kaappi> (define-syntax swap!
         (syntax-rules ()
           ((swap! a b)
            (let ((tmp a))
              (set! a b)
              (set! b tmp)))))
kaappi> (let ((x 1) (y 2))
         (swap! x y)
         (list x y))
;=> (2 1)

See also: syntax-rules, let-syntax


syntax-rules

(syntax-rules (literal ...) (pattern template) ...)

Defines a pattern-based macro transformer. Each pattern is matched against the macro call; the first matching pattern determines the template used to produce the expansion. Identifiers listed as literals are matched by binding rather than treated as pattern variables. Patterns support ... (ellipsis) for matching zero or more subforms.

kaappi> (define-syntax my-if
         (syntax-rules (then else)
           ((my-if test then consequent else alternate)
            (cond (test consequent)
                  (#t alternate)))))
kaappi> (my-if (> 3 2) then 'yes else 'no)
;=> yes
kaappi> (define-syntax my-and
         (syntax-rules ()
           ((my-and) #t)
           ((my-and x) x)
           ((my-and x rest ...)
            (if x (my-and rest ...) #f))))
kaappi> (my-and 1 2 3)
;=> 3

See also: define-syntax, let-syntax


let-syntax

(let-syntax ((name transformer) ...) body ...)

Introduces local macro bindings. Each name is bound to its transformer (typically syntax-rules) for the scope of body. The transformers are evaluated in the enclosing environment -- they cannot reference each other.

kaappi> (let-syntax ((double (syntax-rules ()
                               ((double x) (+ x x)))))
         (double 21))
;=> 42

See also: letrec-syntax, define-syntax


letrec-syntax

(letrec-syntax ((name transformer) ...) body ...)

Like let-syntax, but the transformers can reference each other, enabling mutually recursive macro definitions within body.

kaappi> (letrec-syntax
          ((my-or (syntax-rules ()
                    ((my-or) #f)
                    ((my-or e1 e2 ...)
                     (let ((t e1))
                       (if t t (my-or e2 ...)))))))
         (my-or #f #f 42))
;=> 42

See also: let-syntax, define-syntax


Modules

define-library

(define-library (lib-name ...) declaration ...)

Defines a library (module) with the given hierarchical name. Library declarations include export, import, begin (for definitions), include, and cond-expand. Libraries provide separate namespaces and explicit interfaces.

(define-library (mylib utils)
  (export factorial fibonacci)
  (import (scheme base))
  (begin
    (define (factorial n)
      (if (<= n 1) 1 (* n (factorial (- n 1)))))
    (define (fibonacci n)
      (let loop ((i 0) (a 0) (b 1))
        (if (= i n) a (loop (+ i 1) b (+ a b)))))))

See also: import, cond-expand


import

(import import-set ...)

Imports bindings from one or more libraries into the current environment. Import sets can be plain library names or modified with only, except, prefix, and rename to control which bindings are brought in and under what names.

kaappi> (import (scheme base))
kaappi> (import (scheme write))
kaappi> (import (only (srfi 1) fold filter))
kaappi> (import (prefix (srfi 69) ht:))
kaappi> (import (rename (scheme base) (define def)))

See also: define-library, environment


Exceptions and Control

guard

(guard (var (test expr ...) ...) body ...)

Structured exception handling. Evaluates body; if an exception is raised, it is bound to var and the cond-style clauses are tested in order. If a clause's test is true, its expr sequence is evaluated and the result returned. An else clause catches all exceptions. If no clause matches, the exception is re-raised.

kaappi> (guard (e ((string? e) (string-append "caught: " e))
                  ((number? e) (* e 2)))
         (raise "oops"))
;=> "caught: oops"
kaappi> (guard (e ((number? e) (* e 10)))
         (raise 5))
;=> 50
kaappi> (guard (e (#t (error-object-message e)))
         (error "something failed" 'details))
;=> "something failed"

See also: raise, with-exception-handler, cond


define-record-type

(define-record-type name (constructor field-name ...) predicate (field-name accessor) ... (field-name accessor mutator) ...)

Defines a new record type with a constructor, predicate, and field accessors (and optional mutators). This is the standard way to define structured data types in R7RS.

kaappi> (define-record-type <point>
         (make-point x y)
         point?
         (x point-x)
         (y point-y))
kaappi> (define p (make-point 3 4))
kaappi> (point? p)
;=> #t
kaappi> (point-x p)
;=> 3
kaappi> (point-y p)
;=> 4
kaappi> (point? '(3 4))
;=> #f

See also: define


Lazy Evaluation

delay

(delay expr)

Creates a promise that, when forced, evaluates expr and caches the result. Subsequent calls to force on the same promise return the cached value without re-evaluating expr.

kaappi> (define p (delay (begin (display "computing...") (newline) 42)))
kaappi> (force p)
computing...
;=> 42
kaappi> (force p)
;=> 42

See also: delay-force, force, promise?


delay-force

(delay-force expr)

Creates an iterative promise. When forced, expr is evaluated and must return another promise, which is then forced iteratively (not recursively). This avoids stack overflow when chaining lazy computations. Also known as lazy in SRFI-45.

kaappi> (define (lazy-range n limit)
         (if (>= n limit)
             (delay '())
             (delay-force
               (delay (cons n (lazy-range (+ n 1) limit))))))
kaappi> (force (lazy-range 0 5))
;=> (0 . #<promise>)
kaappi> (define (force-list p)
         (let ((v (force p)))
           (if (null? v) '()
               (cons (car v) (force-list (cdr v))))))
kaappi> (force-list (lazy-range 0 5))
;=> (0 1 2 3 4)

See also: delay, force


parameterize

(parameterize ((param value) ...) body ...)

Dynamically rebinds each parameter object param to value for the duration of body. When control leaves body (normally or via continuation), the parameters are restored to their previous values. If the parameter was created with a converter procedure, the converter is applied to value before binding.

kaappi> (define radix (make-parameter 10))
kaappi> (radix)
;=> 10
kaappi> (parameterize ((radix 16))
         (radix))
;=> 16
kaappi> (radix)
;=> 10
kaappi> (parameterize ((current-output-port (open-output-string)))
         (display "captured")
         (get-output-string (current-output-port)))
;=> "captured"

See also: make-parameter, dynamic-wind


cond-expand

(cond-expand (feature-requirement expr ...) ... (else expr ...))

Compile-time conditional expansion based on implementation features. Each feature-requirement is tested against the features returned by (features). The first matching clause's expr sequence is expanded into the program; other clauses are discarded entirely. Feature requirements can use and, or, not, and library to compose tests.

kaappi> (cond-expand
         (kaappi (display "Running on Kaappi") (newline))
         (else (display "Unknown implementation") (newline)))
Running on Kaappi
kaappi> (cond-expand
         ((library (srfi 1))
          (import (srfi 1))
          (display "SRFI-1 available"))
         (else
          (display "No SRFI-1")))
SRFI-1 available

See also: features, define-library